/* add new name into a directory. If it exists in a directory - do nothing */ int reiserfs_add_entry (reiserfs_filsys_t fs, struct key * dir, char * name, struct key * key, int fsck_need) { struct item_head entry_ih = {{0,}, }; char * entry; int retval; INITIALIZE_PATH(path); int gen_counter; int item_len; __u32 hash; if (reiserfs_find_entry (fs, dir, name, &gen_counter)) return 0; /* compose entry key to look for its place in the tree */ entry_ih.ih_key.k_dir_id = cpu_to_le32 (dir->k_dir_id); entry_ih.ih_key.k_objectid = cpu_to_le32 (dir->k_objectid); hash = hash_value (fs, name) + gen_counter; if (!strcmp (name, ".")) hash = DOT_OFFSET; if (!strcmp (name, "..")) hash = DOT_DOT_OFFSET; set_type_and_offset (KEY_FORMAT_1, &(entry_ih.ih_key), hash, TYPE_DIRENTRY); set_ih_key_format (&entry_ih, KEY_FORMAT_1); set_entry_count (&entry_ih, 1); if (SB_VERSION (fs) == REISERFS_VERSION_2) item_len = DEH_SIZE + ROUND_UP (strlen (name)); else item_len = DEH_SIZE + strlen (name); set_ih_item_len (&entry_ih, item_len); /* fsck may need to insert item which was not reached yet */ set_ih_fsck_need( &entry_ih, fsck_need ); entry = make_entry (0, name, key, get_offset (&(entry_ih.ih_key))); retval = _search_by_entry_key (fs, &(entry_ih.ih_key), &path); switch (retval) { case POSITION_NOT_FOUND: reiserfs_paste_into_item (fs, &path, entry, item_len); break; case DIRECTORY_NOT_FOUND: set_deh_location( (struct reiserfs_de_head *)entry, DEH_SIZE ); reiserfs_insert_item (fs, &path, &entry_ih, entry); break; default: reiserfs_panic ("reiserfs_add_entry: looking for %k (inserting name \"%s\") " "search_by_entry_key returned %d", &(entry_ih.ih_key), name, retval); } freemem (entry); return item_len; }
/* check directory item and try to recover something */ static int verify_directory_item (reiserfs_filsys_t fs, struct buffer_head * bh, int item_num) { struct item_head * ih; struct item_head tmp; char * item; struct reiserfs_de_head * deh; char * name; int name_len; int bad; int i, j; #if 0 int bad_entries; /* how many bad neighboring entries */ int total_entry_len; char * entries, * end; #endif int dirty; int entry_count; int hash_code; int bad_locations; #ifdef DEBUG_VERIFY_DENTRY char * direntries; #endif ih = B_N_PITEM_HEAD (bh, item_num); item = B_I_PITEM (bh,ih); deh = (struct reiserfs_de_head *)item; dirty = 0; bad_locations = 0; entry_count = ih_entry_count (ih); /* check deh_location */ for (i = 0; i < ih_entry_count (ih); i ++) { /* silently fix deh_state */ if (deh [i].deh_state != (1 << DEH_Visible)) { deh [i].deh_state = cpu_to_le16 (1 << DEH_Visible); mark_buffer_dirty (bh); } if (dir_entry_bad_location (deh + i, ih, !i)) mark_de_bad_location (deh + i); } #ifdef DEBUG_VERIFY_DENTRY direntries = getmem (ih_entry_count (ih) * sizeof (int)); printf ("entries with bad locations: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad_location (deh + i)) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ /* find entries names in which have mismatching deh_offset */ for (i = ih_entry_count (ih) - 1; i >= 0; i --) { if (de_bad (deh + i)) /* bad location */ continue; if (i) { if (deh_location (deh + i - 1) < deh_location (deh + i)) mark_de_bad_location (deh + i - 1); } name = name_in_entry (deh + i, i); /* we found a name, but we not always we can get its length as it depends on deh_location of previous entry */ name_len = try_to_get_name_length (ih, deh + i, i); #ifdef DEBUG_VERIFY_DENTRY if (name_len == 0) printf ("trying to find name length for %d-th entry\n", i); #endif /* DEBUG_VERIFY_DENTRY */ if (is_dot (name, name_len)) { if (i != 0) fsck_log ("block %lu: item %d: \".\" is %d-th entry\n", bh->b_blocknr, item_num, i); /* check and fix "." */ if (deh_offset (deh + i) != DOT_OFFSET) { set_deh_offset(&(deh[i]), DOT_OFFSET); mark_buffer_dirty (bh); } /* "." must point to the directory it is in */ if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { /* endian safe */ fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " "pointing to (%K) instead of (%K)\n", bh->b_blocknr, ih, &(deh[i].deh_dir_id), &(ih->ih_key)); deh[i].deh_dir_id = ih->ih_key.k_dir_id; /* both LE */ deh[i].deh_objectid = ih->ih_key.k_objectid; /* both LE */ mark_buffer_dirty (bh); } } else if (is_dot_dot (name, name_len)) { if (i != 1) fsck_log ("block %lu: item %d: \"..\" is %d-th entry\n", bh->b_blocknr, item_num, i); /* check and fix ".." */ if (deh_offset (deh + i) != DOT_DOT_OFFSET) set_deh_offset(&(deh[i]), DOT_DOT_OFFSET); } else { int min_length, max_length; /* check other name */ if (name_len == 0) { /* we do not know the length of name - we will try to find it */ min_length = 1; max_length = item + ih_item_len (ih) - name; } else /* we kow name length, so we will try only one name length */ min_length = max_length = name_len; for (j = min_length; j <= max_length; j ++) { hash_code = find_hash_in_use (name, j, GET_HASH_VALUE (deh_offset (deh + i)), rs_hash (fs->s_rs)); add_hash_hit (fs, hash_code); if (code2func (hash_code) != 0) { /* deh_offset matches to some hash of the name */ if (!name_len) { fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " "matching to deh_offset %u. FIXME: should set deh_location " "of previous entry (not ready)\n", bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ if (i) { set_deh_location( &(deh[i - 1]), deh_location (deh + i) + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); mark_de_good_location (deh + i - 1); mark_buffer_dirty (bh); } } break; } } if (j == max_length + 1) { /* deh_offset does not match to anything. it will be deleted for now, but maybe we could just fix a deh_offset if it is in ordeer */ mark_de_bad_offset (deh + i); } } } /* for */ #if 0 /* find entries names in which have mismatching deh_offset */ for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad (deh + i)) /* bad location */ continue; name = name_in_entry (deh + i, i); /* we found a name, but we not always we can get its length as it depends on deh_location of previous entry */ name_len = try_to_get_name_length (ih, deh + i, i); if (i == 0 && is_dot (name, name_len)) { /* check and fix "." */ if (deh_offset (deh + i) != DOT_OFFSET) { deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET); } /* "." must point to the directory it is in */ if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " "pointing to (%K) instead of (%K)\n", bh->b_blocknr, ih, &(deh[i].deh_dir_id), &(ih->ih_key)); deh[i].deh_dir_id = ih->ih_key.k_dir_id; /* both 32 bit LE */ deh[i].deh_objectid = ih->ih_key.k_objectid; /* both 32 bit LE */ mark_buffer_dirty (bh); } } else if (i == 1 && is_dot_dot (name, name_len)) { /* check and fix ".." */ if (deh_offset (deh + i) != DOT_DOT_OFFSET) deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); } else { int min_length, max_length; /* check other name */ if (name_len == 0) { /* we do not know the length of name - we will try to find it */ min_length = 1; max_length = item + ih_item_len (ih) - name; } else /* we kow name length, so we will try only one name length */ min_length = max_length = name_len; for (j = min_length; j <= max_length; j ++) { hash_code = find_hash_in_use (name, j, GET_HASH_VALUE (deh_offset (deh + i)), rs_hash (fs->s_rs)); add_hash_hit (fs, hash_code); if (code2func (hash_code) != 0) { /* deh_offset matches to some hash of the name */ if (!name_len) { fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " "matching to deh_offset %u. FIXME: should set deh_location " "of previous entry (not ready)\n", bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); } break; } } if (j == max_length + 1) { /* deh_offset does not match to anything. it will be deleted for now, but maybe we could just fix a deh_offset if it is in ordeer */ mark_de_bad_offset (deh + i); } } } #endif #ifdef DEBUG_VERIFY_DENTRY printf ("entries with mismatching deh_offsets: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad_offset (deh + i)) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ /* correct deh_locations such that code cutting entries will not get screwed up */ { int prev_loc; int loc_fixed; prev_loc = ih_item_len (ih); for (i = 0; i < ih_entry_count (ih); i ++) { loc_fixed = 0; if (de_bad_location (deh + i)) { set_deh_location(&(deh[i]), prev_loc/* - 1*/); mark_buffer_dirty (bh); loc_fixed = 1; } else { if (deh_location (deh + i) >= prev_loc) { set_deh_location(&(deh[i]), prev_loc/* - 1*/); mark_buffer_dirty (bh); loc_fixed = 1; } } prev_loc = deh_location (deh + i); if (i == ih_entry_count (ih) - 1) { /* last entry starts right after an array of dir entry headers */ if (!de_bad (deh + i) && deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { /* free space in the directory item */ fsck_log ("verify_direntry: block %lu, item %H has free pace\n", bh->b_blocknr, ih); cut_entry (fs, bh, item_num, ih_entry_count (ih), 0); } if (deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { set_deh_location(&(deh[i]), DEH_SIZE * ih_entry_count (ih)); loc_fixed = 1; mark_buffer_dirty (bh); } } #ifdef DEBUG_VERIFY_DENTRY if (loc_fixed) direntries [i] = 1; #endif } /* for */ #ifdef DEBUG_VERIFY_DENTRY printf ("entries with fixed deh_locations: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (direntries [i]) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ } #ifdef DEBUG_VERIFY_DENTRY printf (" N location name\n"); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad (deh + i) || (i && de_bad (deh + i - 1)) || /* previous entry marked bad */ (i < ih_entry_count (ih) - 1 && de_bad (deh + i + 1))) { /* next ntry is marked bad */ /* print only entries to be deleted and their nearest neighbors */ printf ("%3d: %8d ", i, deh_location (deh + i)); if (de_bad (deh + i)) printf ("will be deleted\n"); else printf ("\"%.*s\"\n", name_length (ih, deh + i, i), name_in_entry (deh + i, i)); } } #endif bad = 0; tmp = *ih; /* delete entries which are marked bad */ for (i = 0; i < ih_entry_count (ih); i ++) { deh = B_I_DEH (bh, ih) + i; if (de_bad (deh)) { bad ++; if (ih_entry_count (ih) == 1) { delete_item (fs, bh, item_num); break; } else { cut_entry (fs, bh, item_num, i, 1); } i --; } } if (bad == ih_entry_count (&tmp)) { fsck_log ("pass0: block %lu, item %H - all entries were deleted\n", bh->b_blocknr, &tmp); return 0; } deh = B_I_DEH (bh, ih); if (get_offset (&ih->ih_key) != deh_offset (deh)) { fsck_log ("verify_direntry: block %lu, item %H: k_offset and deh_offset %u mismatched\n", bh->b_blocknr, ih, deh_offset (deh)); set_offset (KEY_FORMAT_1, &ih->ih_key, deh_offset (deh)); mark_buffer_dirty (bh); } if (bad) fsck_log ("pass0: block %lu, item %H: %d entries were deleted of \n", bh->b_blocknr, &tmp, bad); return 0; #if 0 /* FIXME: temporary */ if (bad_locations > ih_entry_count (ih) / 2) { fsck_log ("pass0: block %u: item %d (%H) had too bad directory - deleted\n", bh->b_blocknr, item_num, ih); delete_item (fs, bh, item_num); return 0; } if (!dirty) return 0; /* something is broken */ fsck_log ("pass0: block %lu: %d-th item (%H) has %d bad entries..", bh->b_blocknr, item_num, ih, dirty); if (get_offset (&ih->ih_key) == DOT_OFFSET) { /* first item of directory - make sure that "." and ".." are in place */ if (deh_offset (deh) != DOT_OFFSET || name_in_entry (deh, 0)[0] != '.') { set_deh_offset(deh, DOT_OFFSET); name_in_entry (deh, 0)[0] = '.'; } if (deh_offset (deh + 1) != DOT_DOT_OFFSET || name_in_entry (deh + 1, 1)[0] != '.' || name_in_entry (deh + 1, 1)[1] != '.') { set_deh_offset((deh + 1), DOT_DOT_OFFSET); name_in_entry (deh + 1, 1)[0] = '.'; name_in_entry (deh + 1, 1)[1] = '.'; } } end = item + ih_item_len (ih); deh += ih_entry_count (ih); entries = (char *)deh; total_entry_len = ih_item_len (ih) - DEH_SIZE * ih_entry_count (ih); i = ih_entry_count (ih); bad_entries = 0; do { i --; deh --; name_len = prob_name (fs, &entries, total_entry_len, deh_offset (deh)); if (!name_len) { if (!bad_entries) { set_deh_location(deh, entries - item); } else { set_deh_location(deh, deh_location((deh + 1)) + 1 ); } bad_entries ++; /*fsck_log ("verify_directory_item: entry %d: in string \'%s\' there is no substring matching hash %ld\n", i, bad_name (entries, total_entry_len), masked_offset);*/ mark_de_bad (deh); continue; } bad_entries = 0; /*fsck_log ("verify_directory_item: entry %d: found \"%s\" name matching hash %ld\n", i, bad_name (entries, name_len), masked_offset);*/ /* 'entries' points now to the name which match given offset - so, set deh_location */ set_deh_location(deh, entries - item); deh->deh_state = 0; mark_de_visible (deh); entries += name_len; total_entry_len = end - entries; /* skip padding zeros */ while (!*entries) { entries ++; total_entry_len --; } /* 'entries' points now at the place where next (previous) entry should start */ } while ((char *)deh != item); /* fixme: this will not work if all entries are to be deleted */ for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { deh = (struct reiserfs_de_head *)B_I_PITEM (bh, ih) + i; if (de_bad (deh)) { if (ih_entry_count (ih) == 1) { delete_item (fs, bh, i); break; } else { cut_entry (fs, bh, item_num, i); } i --; } /* fsck_log ("verify_directory_item: %d-th entry is to be deleted: " "\"%s\" does not match to hash %lu\n", i, bad_name (name_in_entry (deh, i), name_length (ih, deh, i)), deh_offset (deh)); */ } fsck_log ("%d entries were deleted\n", entry_count - ih_entry_count (ih)); mark_buffer_dirty (bh); return 0; #endif }
/* * Common code for mount and mountroot */ static int reiserfs_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { int error, old_format = 0; struct reiserfs_mount *rmp; struct reiserfs_sb_info *sbi; struct reiserfs_super_block *rs; struct cdev *dev; struct g_consumer *cp; struct bufobj *bo; //ronly = (mp->mnt_flag & MNT_RDONLY) != 0; dev = devvp->v_rdev; dev_ref(dev); DROP_GIANT(); g_topology_lock(); error = g_vfs_open(devvp, &cp, "reiserfs", /* read-only */ 0); g_topology_unlock(); PICKUP_GIANT(); VOP_UNLOCK(devvp, 0); if (error) { dev_rel(dev); return (error); } bo = &devvp->v_bufobj; bo->bo_private = cp; bo->bo_ops = g_vfs_bufops; if (devvp->v_rdev->si_iosize_max != 0) mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; if (mp->mnt_iosize_max > MAXPHYS) mp->mnt_iosize_max = MAXPHYS; rmp = NULL; sbi = NULL; /* rmp contains any information about this specific mount */ rmp = malloc(sizeof *rmp, M_REISERFSMNT, M_WAITOK | M_ZERO); if (!rmp) { error = (ENOMEM); goto out; } sbi = malloc(sizeof *sbi, M_REISERFSMNT, M_WAITOK | M_ZERO); if (!sbi) { error = (ENOMEM); goto out; } rmp->rm_reiserfs = sbi; rmp->rm_mountp = mp; rmp->rm_devvp = devvp; rmp->rm_dev = dev; rmp->rm_bo = &devvp->v_bufobj; rmp->rm_cp = cp; /* Set default values for options: non-aggressive tails */ REISERFS_SB(sbi)->s_mount_opt = (1 << REISERFS_SMALLTAIL); REISERFS_SB(sbi)->s_rd_only = 1; REISERFS_SB(sbi)->s_devvp = devvp; /* Read the super block */ if ((error = read_super_block(rmp, REISERFS_OLD_DISK_OFFSET)) == 0) { /* The read process succeeded, it's an old format */ old_format = 1; } else if ((error = read_super_block(rmp, REISERFS_DISK_OFFSET)) != 0) { reiserfs_log(LOG_ERR, "can not find a ReiserFS filesystem\n"); goto out; } rs = SB_DISK_SUPER_BLOCK(sbi); /* * Let's do basic sanity check to verify that underlying device is * not smaller than the filesystem. If the check fails then abort and * scream, because bad stuff will happen otherwise. */ #if 0 if (s->s_bdev && s->s_bdev->bd_inode && i_size_read(s->s_bdev->bd_inode) < sb_block_count(rs) * sb_blocksize(rs)) { reiserfs_log(LOG_ERR, "reiserfs: filesystem cannot be mounted because it is " "bigger than the device.\n"); reiserfs_log(LOG_ERR, "reiserfs: you may need to run fsck " "rr may be you forgot to reboot after fdisk when it " "told you to.\n"); goto out; } #endif /* * XXX This is from the original Linux code, but why affecting 2 values * to the same variable? */ sbi->s_mount_state = SB_REISERFS_STATE(sbi); sbi->s_mount_state = REISERFS_VALID_FS; if ((error = (old_format ? read_old_bitmaps(rmp) : read_bitmaps(rmp)))) { reiserfs_log(LOG_ERR, "unable to read bitmap\n"); goto out; } /* Make data=ordered the default */ if (!reiserfs_data_log(sbi) && !reiserfs_data_ordered(sbi) && !reiserfs_data_writeback(sbi)) { REISERFS_SB(sbi)->s_mount_opt |= (1 << REISERFS_DATA_ORDERED); } if (reiserfs_data_log(sbi)) { reiserfs_log(LOG_INFO, "using journaled data mode\n"); } else if (reiserfs_data_ordered(sbi)) { reiserfs_log(LOG_INFO, "using ordered data mode\n"); } else { reiserfs_log(LOG_INFO, "using writeback data mode\n"); } /* TODO Not yet supported */ #if 0 if(journal_init(sbi, jdev_name, old_format, commit_max_age)) { reiserfs_log(LOG_ERR, "unable to initialize journal space\n"); goto out; } else { jinit_done = 1 ; /* once this is set, journal_release must be called if we error out of the mount */ } if (reread_meta_blocks(sbi)) { reiserfs_log(LOG_ERR, "unable to reread meta blocks after journal init\n"); goto out; } #endif /* Define and initialize hash function */ sbi->s_hash_function = hash_function(rmp); if (sbi->s_hash_function == NULL) { reiserfs_log(LOG_ERR, "couldn't determined hash function\n"); error = (EINVAL); goto out; } if (is_reiserfs_3_5(rs) || (is_reiserfs_jr(rs) && SB_VERSION(sbi) == REISERFS_VERSION_1)) bit_set(&(sbi->s_properties), REISERFS_3_5); else bit_set(&(sbi->s_properties), REISERFS_3_6); mp->mnt_data = rmp; mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; mp->mnt_kern_flag |= MNTK_MPSAFE; MNT_IUNLOCK(mp); #if defined(si_mountpoint) devvp->v_rdev->si_mountpoint = mp; #endif return (0); out: reiserfs_log(LOG_INFO, "*** error during mount ***\n"); if (sbi) { if (SB_AP_BITMAP(sbi)) { int i; for (i = 0; i < SB_BMAP_NR(sbi); i++) { if (!SB_AP_BITMAP(sbi)[i].bp_data) break; free(SB_AP_BITMAP(sbi)[i].bp_data, M_REISERFSMNT); } free(SB_AP_BITMAP(sbi), M_REISERFSMNT); } if (sbi->s_rs) { free(sbi->s_rs, M_REISERFSMNT); sbi->s_rs = NULL; } } if (cp != NULL) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); } if (sbi) free(sbi, M_REISERFSMNT); if (rmp) free(rmp, M_REISERFSMNT); dev_rel(dev); return (error); }