/* * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ #include "includes.h" /* fixme: this assumes that journal start and journal size are set correctly */ static void check_first_bitmap (reiserfs_filsys_t fs, char * bitmap) { int i; int bad; bad = 0; for (i = 0; i < rs_journal_start (fs->s_rs) + rs_journal_size (fs->s_rs) + 1; i ++) { if (!test_bit (i, bitmap)) { bad = 1; /*reiserfs_warning ("block %d is marked free in the first bitmap, fixed\n", i);*/ /*set_bit (i, bitmap);*/ } } if (bad) reiserfs_warning (stderr, "reiserfs_open: first bitmap looks corrupted\n"); } /* read bitmap blocks */ void reiserfs_read_bitmap_blocks (reiserfs_filsys_t fs) { struct reiserfs_super_block * rs = fs->s_rs; struct buffer_head * bh = SB_BUFFER_WITH_SB(fs); int fd = fs->s_dev; unsigned long block; int i; /* read bitmaps, and correct a bit if necessary */ SB_AP_BITMAP (fs) = getmem (sizeof (void *) * rs_bmap_nr (rs)); for (i = 0, block = bh->b_blocknr + 1; i < rs_bmap_nr (rs); i ++) { SB_AP_BITMAP (fs)[i] = bread (fd, block, fs->s_blocksize); if (!SB_AP_BITMAP (fs)[i]) { reiserfs_warning (stderr, "reiserfs_open: bread failed reading bitmap #%d (%lu)\n", i, block); SB_AP_BITMAP (fs)[i] = getblk (fd, block, fs->s_blocksize); memset (SB_AP_BITMAP (fs)[i]->b_data, 0xff, fs->s_blocksize); set_bit (BH_Uptodate, &SB_AP_BITMAP (fs)[i]->b_state); } /* all bitmaps have to have itself marked used on it */ if (bh->b_blocknr == 16) { if (!test_bit (block % (fs->s_blocksize * 8), SB_AP_BITMAP (fs)[i]->b_data)) { reiserfs_warning (stderr, "reiserfs_open: bitmap %d was marked free\n", i); /*set_bit (block % (fs->s_blocksize * 8), SB_AP_BITMAP (fs)[i]->b_data);*/ } } else { /* bitmap not spread over partition: fixme: does not work when number of bitmaps => 32768 */ if (!test_bit (block, SB_AP_BITMAP (fs)[0]->b_data)) { reiserfs_warning (stderr, "reiserfs_open: bitmap %d was marked free\n", i); /*set_bit (block, SB_AP_BITMAP (fs)[0]->b_data);*/ } } if (i == 0) { /* first bitmap has to have marked used super block and journal areas */ check_first_bitmap (fs, SB_AP_BITMAP (fs)[i]->b_data); } block = (bh->b_blocknr == 16 ? ((i + 1) * fs->s_blocksize * 8) : (block + 1)); } } void reiserfs_free_bitmap_blocks (reiserfs_filsys_t fs) { int i; /* release bitmaps if they were read */ if (SB_AP_BITMAP (fs)) { for (i = 0; i < SB_BMAP_NR (fs); i ++) brelse (SB_AP_BITMAP (fs) [i]); freemem (SB_AP_BITMAP (fs)); } } /* read super block and bitmaps. fixme: only 4k blocks, pre-journaled format is refused */ reiserfs_filsys_t reiserfs_open (char * filename, int flags, int *error, void * vp) { reiserfs_filsys_t fs; struct buffer_head * bh; struct reiserfs_super_block * rs; int fd, i; fd = open (filename, flags | O_LARGEFILE); if (fd == -1) { if (error) *error = errno; return 0; } fs = getmem (sizeof (*fs)); fs->s_dev = fd; fs->s_vp = vp; asprintf (&fs->file_name, "%s", filename); /* reiserfs super block is either in 16-th or in 2-nd 4k block of the device */ for (i = 16; i > 0; i -= 14) { bh = bread (fd, i, 4096); if (!bh) { reiserfs_warning (stderr, "reiserfs_open: bread failed reading block %d\n", i); } else { rs = (struct reiserfs_super_block *)bh->b_data; if (is_reiser2fs_magic_string (rs) || is_reiserfs_magic_string (rs)) goto found; /* reiserfs signature is not found at the i-th 4k block */ brelse (bh); } } reiserfs_warning (stderr, "reiserfs_open: neither new nor old reiserfs format " "found on %s\n", filename); if (error) *error = 0; return fs; found: /* fixme: we could make some check to make sure that super block looks correctly */ fs->s_version = is_reiser2fs_magic_string (rs) ? REISERFS_VERSION_2 : REISERFS_VERSION_1; fs->s_blocksize = rs_blocksize (rs); fs->s_hash_function = code2func (rs_hash (rs)); SB_BUFFER_WITH_SB (fs) = bh; fs->s_rs = rs; fs->s_flags = flags; /* O_RDONLY or O_RDWR */ fs->s_vp = vp; reiserfs_read_bitmap_blocks(fs); return fs; } int no_reiserfs_found (reiserfs_filsys_t fs) { return (fs->s_blocksize == 0) ? 1 : 0; } int new_format (reiserfs_filsys_t fs) { return fs->s_sbh->b_blocknr != 2; } int spread_bitmaps (reiserfs_filsys_t fs) { return fs->s_sbh->b_blocknr != 2; } void reiserfs_reopen (reiserfs_filsys_t fs, int flag) { close (fs->s_dev); fs->s_dev = open (fs->file_name, flag | O_LARGEFILE); if (fs->s_dev == -1) die ("reiserfs_reopen: could not reopen device: %m"); } int filesystem_dirty (reiserfs_filsys_t fs) { return fs->s_dirt; } void mark_filesystem_dirty (reiserfs_filsys_t fs) { fs->s_dirt = 1; } /* flush all changes made on a filesystem */ void reiserfs_flush (reiserfs_filsys_t fs) { flush_buffers (); } /* free all memory involved into manipulating with filesystem */ void reiserfs_free (reiserfs_filsys_t fs) { reiserfs_free_bitmap_blocks(fs); /* release super block and memory used by filesystem handler */ brelse (SB_BUFFER_WITH_SB (fs)); free_buffers (); free (fs->file_name); freemem (fs); } void reiserfs_close (reiserfs_filsys_t fs) { reiserfs_flush (fs); reiserfs_free (fs); } int reiserfs_new_blocknrs (reiserfs_filsys_t fs, unsigned long * free_blocknrs, unsigned long start, int amount_needed) { if (fs->block_allocator) return fs->block_allocator (fs, free_blocknrs, start, amount_needed); die ("block allocator is not defined\n"); return 0; } int reiserfs_free_block (reiserfs_filsys_t fs, unsigned long block) { if (fs->block_deallocator) return fs->block_deallocator (fs, block); die ("block allocator is not defined\n"); return 0; } typedef int (comp_function_t) (void * key1, void * key2); inline int _bin_search (void * key, void * base, int num, int width, __u32 *ppos, comp_function_t comp_func) { int rbound, lbound, j; if (num == 0) { /* objectid map may be 0 elements long */ *ppos = 0; return ITEM_NOT_FOUND; } lbound = 0; rbound = num - 1; for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { switch (comp_func ((void *)((char *)base + j * width), key ) ) { case -1:/* second is greater */ lbound = j + 1; continue; case 1: /* first is greater */ if (j == 0) { *ppos = lbound; return ITEM_NOT_FOUND; } rbound = j - 1; continue; case 0: *ppos = j; return ITEM_FOUND; } } *ppos = lbound; return ITEM_NOT_FOUND; } #if 0 static inline int _bin_search (void * key, void * base, int num, int width, __u32 *ppos) { __u32 rbound, lbound, j; lbound = 0; rbound = num - 1; for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { switch (comp_keys ((void *)((char *)base + j * width), key)) { case -1:/* second is greater */ lbound = j + 1; continue; case 1: /* first is greater */ if (j == 0) { *ppos = lbound; return ITEM_NOT_FOUND; } rbound = j - 1; continue; case 0: *ppos = j; return ITEM_FOUND; } } *ppos = lbound; return ITEM_NOT_FOUND; } #endif static int _search_by_key (reiserfs_filsys_t fs, struct key * key, struct path * path) { struct buffer_head * bh; unsigned long block = SB_ROOT_BLOCK (fs); struct path_element * curr; int retval; path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET; while (1) { curr = PATH_OFFSET_PELEMENT (path, ++ path->path_length); bh = curr->pe_buffer = bread (fs->s_dev, block, fs->s_blocksize); if (bh == 0) { path->path_length --; pathrelse (path); return ITEM_NOT_FOUND; } retval = _bin_search (key, B_N_PKEY (bh, 0), B_NR_ITEMS (bh), is_leaf_node (bh) ? IH_SIZE : KEY_SIZE, &(curr->pe_position), comp_keys); if (retval == ITEM_FOUND) { /* key found, return if this is leaf level */ if (is_leaf_node (bh)) { path->pos_in_item = 0; return ITEM_FOUND; } curr->pe_position ++; } else { /* key not found in the node */ if (is_leaf_node (bh)) return ITEM_NOT_FOUND; } block = B_N_CHILD_NUM (bh, curr->pe_position); } printf ("search_by_key: you can not get here\n"); return ITEM_NOT_FOUND; } static int comp_dir_entries (void * p1, void * p2) { __u32 deh_offset; __u32 * off1, * off2; off1 = p1; off2 = p2; deh_offset = le32_to_cpu (*off1); if (deh_offset < *off2) return -1; if (deh_offset > *off2) return 1; return 0; } static struct key * _get_rkey (struct path * path) { int pos, offset = path->path_length; struct buffer_head * bh; if (offset < FIRST_PATH_ELEMENT_OFFSET) die ("_get_rkey: illegal offset in the path (%d)", offset); while (offset-- > FIRST_PATH_ELEMENT_OFFSET) { if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset))) die ("_get_rkey: parent is not uptodate"); /* Parent at the path is not in the tree now. */ if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset))) die ("_get_rkey: buffer on the path is not in tree"); /* Check whether position in the parrent is correct. */ if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh)) die ("_get_rkey: invalid position (%d) in the path", pos); /* Check whether parent at the path really points to the child. */ if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr) die ("_get_rkey: invalid block number (%d). Must be %d", B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr); /* Return delimiting key if position in the parent is not the last one. */ if (pos != B_NR_ITEMS (bh)) return B_N_PDELIM_KEY (bh, pos); } /* there is no right delimiting key */ return 0; } /* NOTE: this only should be used to look for keys who exists */ int _search_by_entry_key (reiserfs_filsys_t fs, struct key * key, struct path * path) { struct buffer_head * bh; int item_pos; struct item_head * ih; struct key tmpkey; if (_search_by_key (fs, key, path) == ITEM_FOUND) { path->pos_in_item = 0; return POSITION_FOUND; } bh = get_bh (path); item_pos = get_item_pos (path); ih = get_ih (path); if (item_pos == 0) { /* key is less than the smallest key in the tree */ if (not_of_one_file (&(ih->ih_key), key)) /* there are no items of that directory */ return DIRECTORY_NOT_FOUND; if (!is_direntry_ih (ih)) reiserfs_panic ("_search_by_entry_key: found item is not of directory type %H", ih); /* key we looked for should be here */ path->pos_in_item = 0; return POSITION_NOT_FOUND; } /* take previous item */ item_pos --; ih --; PATH_LAST_POSITION (path) --; if (not_of_one_file (&(ih->ih_key), key) || !is_direntry_ih (ih)) { /* previous item belongs to another object or is stat data, check next item */ item_pos ++; PATH_LAST_POSITION (path) ++; if (item_pos < B_NR_ITEMS (bh)) { /* next item is in the same node */ ih ++; if (not_of_one_file (&(ih->ih_key), key)) { /* there are no items of that directory */ path->pos_in_item = 0; return DIRECTORY_NOT_FOUND; } if (!is_direntry_ih (ih)) reiserfs_panic ("_search_by_entry_key: %k is not a directory", key); } else { /* next item is in right neighboring node */ struct key * next_key = _get_rkey (path); if (next_key == 0 || not_of_one_file (next_key, key)) { /* there are no items of that directory */ path->pos_in_item = 0; return DIRECTORY_NOT_FOUND; } if (!is_direntry_key (next_key)) reiserfs_panic ("_search_by_entry_key: %k is not a directory", key); /* we got right delimiting key - search for it - the entry will be pasted in position 0 */ copy_key (&tmpkey, next_key); pathrelse (path); if (_search_by_key (fs, &tmpkey, path) != ITEM_FOUND || PATH_LAST_POSITION (path) != 0) reiserfs_panic ("_search_by_entry_key: item corresponding to delimiting key %k not found", &tmpkey); } /* next item is the part of this directory */ path->pos_in_item = 0; return POSITION_NOT_FOUND; } /* previous item is part of desired directory */ if (_bin_search (&(key->u.k_offset_v1.k_offset), B_I_DEH (bh, ih), ih_entry_count (ih), DEH_SIZE, &(path->pos_in_item), comp_dir_entries) == ITEM_FOUND) return POSITION_FOUND; return POSITION_NOT_FOUND; } static void _init_tb_struct (struct tree_balance * tb, reiserfs_filsys_t fs, struct path * path, int size) { memset (tb, '\0', sizeof(struct tree_balance)); tb->tb_sb = fs; tb->tb_path = path; PATH_OFFSET_PBUFFER(path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL; PATH_OFFSET_POSITION(path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0; tb->insert_size[0] = size; } int reiserfs_remove_entry (reiserfs_filsys_t fs, struct key * key) { struct path path; struct tree_balance tb; struct item_head * ih; struct reiserfs_de_head * deh; if (_search_by_entry_key (fs, key, &path) != POSITION_FOUND) { pathrelse (&path); return 1; } ih = get_ih (&path); if (ih_entry_count (ih) == 1) { _init_tb_struct (&tb, fs, &path, -(IH_SIZE + ih_item_len (ih))); if (fix_nodes (M_DELETE, &tb, 0) != CARRY_ON) { unfix_nodes (&tb); return 1; } do_balance (&tb, 0, 0, M_DELETE, 0); return 0; } deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item; _init_tb_struct (&tb, fs, &path, -(DEH_SIZE + entry_length (ih, deh, path.pos_in_item))); if (fix_nodes (M_CUT, &tb, 0) != CARRY_ON) { unfix_nodes (&tb); return 1; } do_balance (&tb, 0, 0, M_CUT, 0); return 0; } void reiserfs_paste_into_item (reiserfs_filsys_t fs, struct path * path, const void * body, int size) { struct tree_balance tb; _init_tb_struct (&tb, fs, path, size); if (fix_nodes (M_PASTE, &tb, 0/*ih*/) != CARRY_ON) reiserfs_panic ("reiserfs_paste_into_item: fix_nodes failed"); do_balance (&tb, 0, body, M_PASTE, 0/*zero num*/); } void reiserfs_insert_item (reiserfs_filsys_t fs, struct path * path, struct item_head * ih, const void * body) { struct tree_balance tb; _init_tb_struct (&tb, fs, path, IH_SIZE + ih_item_len(ih)); if (fix_nodes (M_INSERT, &tb, ih) != CARRY_ON) die ("reiserfs_insert_item: fix_nodes failed"); do_balance (&tb, ih, body, M_INSERT, 0/*zero num*/); } /*===========================================================================*/ static __u32 hash_value (reiserfs_filsys_t fs, char * name) { __u32 res; if (!strcmp (name, ".")) return DOT_OFFSET; if (!strcmp (name, "..")) return DOT_DOT_OFFSET; res = reiserfs_hash (fs) (name, strlen (name)); res = GET_HASH_VALUE(res); if (res == 0) res = 128; return res; } /* returns 0 if name is not found in a directory and objectid of pointed object otherwise and returns minimal not used generation counter. dies if found object is not a directory. */ int reiserfs_find_entry (reiserfs_filsys_t fs, struct key * dir, char * name, int * min_gen_counter) { struct key entry_key; int retval; int i; INITIALIZE_PATH (path); struct item_head * ih; struct reiserfs_de_head * deh; struct key * rdkey; __u32 hash; entry_key.k_dir_id = dir->k_dir_id; entry_key.k_objectid = dir->k_objectid; hash = hash_value (fs, name); set_type_and_offset (KEY_FORMAT_1, &entry_key, hash, TYPE_DIRENTRY); *min_gen_counter = 0; if (_search_by_entry_key (fs, &entry_key, &path) == DIRECTORY_NOT_FOUND) { pathrelse (&path); return 0; } do { ih = get_ih (&path); deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item; for (i = path.pos_in_item; i < ih_entry_count (ih); i ++, deh ++) { if (GET_HASH_VALUE (deh_offset (deh)) != GET_HASH_VALUE (hash)) { /* all entries having the same hash were scanned */ pathrelse (&path); return 0; } if (GET_GENERATION_NUMBER (deh_offset (deh)) == *min_gen_counter) (*min_gen_counter) ++; if (!memcmp (name_in_entry (deh, i), name, strlen (name))) { pathrelse (&path); return deh_objectid (deh) ? deh_objectid (deh) : 1; } } rdkey = _get_rkey (&path); if (!rdkey || not_of_one_file (rdkey, dir)) { pathrelse (&path); return 0; } if (!is_direntry_key (rdkey)) reiserfs_panic ("reiserfs_find_entry: can not find name in broken directory yet"); /* next item is the item of the directory we are looking name in */ if (GET_HASH_VALUE (get_offset (rdkey)) != hash) { /* but there is no names with given hash */ pathrelse (&path); return 0; } /* first name of that item may be a name we are looking for */ entry_key = *rdkey; pathrelse (&path); retval = _search_by_entry_key (fs, &entry_key, &path); if (retval != POSITION_FOUND) reiserfs_panic ("reiserfs_find_entry: wrong delimiting key in the tree"); } while (1); return 0; } /* compose directory entry: dir entry head and name itself */ char * make_entry (char * entry, char * name, struct key * key, __u32 offset) { struct reiserfs_de_head * deh; if (!entry) entry = getmem (DEH_SIZE + ROUND_UP (strlen (name))); memset (entry, 0, DEH_SIZE + ROUND_UP (strlen (name))); deh = (struct reiserfs_de_head *)entry; deh->deh_location = 0; deh->deh_offset = cpu_to_le32 (offset); deh->deh_state = 0; mark_de_visible (deh); /* key of object entry will point to */ deh->deh_dir_id = cpu_to_le32 (key->k_dir_id); deh->deh_objectid = cpu_to_le32 (key->k_objectid); memcpy ((char *)(deh + 1), name, strlen (name)); return entry; } /* 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_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 */ entry_ih.ih_format.fsck_need = 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: ((struct reiserfs_de_head *)entry)->deh_location = cpu_to_le16 (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; } void copy_key (void * to, void * from) { memcpy (to, from, KEY_SIZE); } void copy_short_key (void * to, void * from) { memcpy (to, from, SHORT_KEY_SIZE); } void copy_item_head(void * p_v_to, void * p_v_from) { memcpy (p_v_to, p_v_from, IH_SIZE); }