/* * balance() * * Description: * Attempt to equalize space usage in neighboring bnodes. * Input Variable(s): * struct hfs_bnode *left: the left bnode. * struct hfs_bnode *right: the right bnode. * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'left' and 'right' point to valid (struct hfs_bnode)s obtained * with HFS_LOCK_WRITE access, and are neighbors. * Postconditions: * Records are shifted either left or right to make the space usage * nearly equal. When exact equality is not possible the break * point is chosen to reduce data movement. * The key corresponding to 'right' in its parent is NOT updated. */ static void balance(struct hfs_bnode *left, struct hfs_bnode *right) { int index, left_free, right_free, half; left_free = bnode_freespace(left); right_free = bnode_freespace(right); half = (left_free + right_free)/2; if (left_free < right_free) { /* shift right to balance */ index = left->ndNRecs + 1; while (right_free >= half) { --index; right_free -= bnode_rsize(left,index)+sizeof(hfs_u16); } if (index < left->ndNRecs) { #if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) hfs_warn("shifting %d of %d recs right to balance: ", left->ndNRecs - index, left->ndNRecs); #endif hfs_bnode_shift_right(left, right, index+1); #if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs); #endif } } else { /* shift left to balance */ index = 0; while (left_free >= half) { ++index; left_free -= bnode_rsize(right,index)+sizeof(hfs_u16); } if (index > 1) { #if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) hfs_warn("shifting %d of %d recs left to balance: ", index-1, right->ndNRecs); #endif hfs_bnode_shift_left(left, right, index-1); #if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs); #endif } } }
/* * parse_new_part_table() * * Parse a new style partition map looking for the * start and length of the 'part'th HFS partition. */ static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, int part, hfs_s32 *size, hfs_s32 *start) { struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf); hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt); int hfs_part = 0; int entry; for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) { if (entry) { /* read the next partition map entry */ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1); if (!hfs_buffer_ok(buf)) { hfs_warn("hfs_fs: unable to " "read partition map.\n"); goto bail; } pm = (struct new_pmap *)hfs_buffer_data(buf); if (hfs_get_ns(pm->pmSig) != htons(HFS_NEW_PMAP_MAGIC)) { hfs_warn("hfs_fs: invalid " "entry in partition map\n"); hfs_buffer_put(buf); goto bail; } } /* look for an HFS partition */ if (!memcmp(pm->pmPartType,"Apple_HFS",9) && ((hfs_part++) == part)) { /* Found it! */ *start = hfs_get_hl(pm->pmPyPartStart); *size = hfs_get_hl(pm->pmPartBlkCnt); } hfs_buffer_put(buf); } return 0; bail: return 1; }
/* * delete_extent() * * Description: * Deletes an extent record from a fork, reducing its physical length. * Input Variable(s): * struct hfs_fork *fork: the fork * struct hfs_extent *ext: the current last extent for 'fork' * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'fork' points to a valid (struct hfs_fork) * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork' * and which is not also the first extent in 'fork'. * Postconditions: * The extent record has been removed if possible, and a warning has been * printed otherwise. */ static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext) { struct hfs_mdb *mdb = fork->entry->mdb; struct hfs_ext_key key; int error; if (fork->cache == ext) { set_cache(fork, ext->prev); } ext->prev->next = NULL; if (ext->count != 1) { hfs_warn("hfs_truncate: extent has count %d.\n", ext->count); } lock_bitmap(mdb); error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } unlock_bitmap(mdb); build_key(&key, fork, ext->start); error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key)); if (error) { hfs_warn("hfs_truncate: error %d deleting an extent.\n", error); } HFS_DELETE(ext); }
/* * cap_info_write() * * This is the write() entry in the file_operations structure for CAP * metadata files. The purpose is to transfer up to 'count' bytes * to the file corresponding to 'inode' beginning at offset * '*ppos' from user-space at the address 'buf'. * The return value is the number of bytes actually transferred. */ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, hfs_rwarg_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; hfs_u32 pos; if (!S_ISREG(inode->i_mode)) { hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode); return -EINVAL; } if (count <= 0) { return 0; } pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos; if (pos > HFS_FORK_MAX) { return 0; } *ppos += count; if (*ppos > HFS_FORK_MAX) { *ppos = HFS_FORK_MAX; count = HFS_FORK_MAX - pos; } if (*ppos > inode->i_size) inode->i_size = *ppos; /* Only deal with the part we store in memory */ if (pos < sizeof(struct hfs_cap_info)) { int end, mem_count; struct hfs_cat_entry *entry = HFS_I(inode)->entry; struct hfs_cap_info meta; mem_count = sizeof(struct hfs_cap_info) - pos; if (mem_count > count) { mem_count = count; } end = pos + mem_count; cap_build_meta(&meta, entry); mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count); /* Update finder attributes if changed */ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) { memcpy(&entry->info, meta.fi_fndr, 32); hfs_cat_mark_dirty(entry); }
/* * hfs_bnode_shift_right() * * Description: * Shifts some records from a node to its right neighbor. * Input Variable(s): * struct hfs_bnode* left: the node to shift records from * struct hfs_bnode* right: the node to shift records to * hfs_u16 first: the number of the first record in 'left' to move to 'right' * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'left' and 'right' point to valid (struct hfs_bnode)s. * 'left' contains at least 'first' records. * 'right' has enough free space to hold the records to be moved from 'left' * Postconditions: * The record numbered 'first' and all records after it in 'left' are * placed at the beginning of 'right'. * The key corresponding to 'right' in its parent is NOT updated. */ void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right, int first) { int i, adjust, nrecs; unsigned size; hfs_u16 *to, *from; if ((first <= 0) || (first > left->ndNRecs)) { hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n", first, left->ndNRecs); return; } /* initialize variables */ nrecs = left->ndNRecs + 1 - first; size = bnode_end(left) - bnode_offset(left, first); /* move (possibly empty) contents of right node forward */ memmove(bnode_datastart(right) + size, bnode_datastart(right), bnode_end(right) - sizeof(struct NodeDescriptor)); /* copy in new records */ memcpy(bnode_datastart(right), bnode_key(left,first), size); /* fix up offsets in right node */ i = right->ndNRecs + 1; from = RECTBL(right, i); to = from - nrecs; while (i--) { hfs_put_hs(hfs_get_hs(from++) + size, to++); } adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first); i = nrecs-1; from = RECTBL(left, first+i); while (i--) { hfs_put_hs(hfs_get_hs(from++) + adjust, to++); } /* fix record counts */ left->ndNRecs -= nrecs; right->ndNRecs += nrecs; }
/* * hfs_bnode_shift_left() * * Description: * Shifts some records from a node to its left neighbor. * Input Variable(s): * struct hfs_bnode* left: the node to shift records to * struct hfs_bnode* right: the node to shift records from * hfs_u16 last: the number of the last record in 'right' to move to 'left' * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'left' and 'right' point to valid (struct hfs_bnode)s. * 'right' contains at least 'last' records. * 'left' has enough free space to hold the records to be moved from 'right' * Postconditions: * The record numbered 'last' and all records before it in 'right' are * placed at the end of 'left'. * The key corresponding to 'right' in its parent is NOT updated. */ void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right, int last) { int i, adjust, nrecs; unsigned size; hfs_u16 *to, *from; if ((last <= 0) || (last > right->ndNRecs)) { hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n", last, right->ndNRecs); return; } /* initialize variables */ size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor); /* copy records to left node */ memcpy(bnode_dataend(left), bnode_datastart(right), size); /* move (possibly empty) remainder of right node backward */ memmove(bnode_datastart(right), bnode_datastart(right) + size, bnode_end(right) - bnode_offset(right, last + 1)); /* fix up offsets */ nrecs = left->ndNRecs; i = last; from = RECTBL(right, 2); to = RECTBL(left, nrecs + 2); adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor); while (i--) { hfs_put_hs(hfs_get_hs(from--) + adjust, to--); } i = right->ndNRecs + 1 - last; ++from; to = RECTBL(right, 1); while (i--) { hfs_put_hs(hfs_get_hs(from--) - size, to--); } /* fix record counts */ left->ndNRecs += last; right->ndNRecs -= last; }
/* * hfs_bdelete() * * Delete the requested record from a B-tree. */ int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key) { struct hfs_belem *belem; struct hfs_bnode *bnode; struct hfs_brec brec; int retval; if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) { hfs_warn("hfs_bdelete: invalid arguments.\n"); return -EINVAL; } retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE); if (!retval) { belem = brec.bottom; bnode = belem->bnr.bn; belem->flags = 0; if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) - bnode_rsize(bnode, belem->record)) < FULL/2) { belem->flags |= HFS_BPATH_UNDERFLOW; } if (belem->record == 1) { belem->flags |= HFS_BPATH_FIRST; } if (!belem->flags) { hfs_brec_lock(&brec, brec.bottom); } else { hfs_brec_lock(&brec, NULL); } retval = bdelete(&brec); if (!retval) { --brec.tree->bthNRecs; brec.tree->dirt = 1; } hfs_brec_relse(&brec, NULL); } return retval; }
/* * hfs_bnode_ditch() * * Description: * This function deletes an entire linked list of bnodes, so it * does not need to keep the linked list consistent as * hfs_bnode_delete() does. * Called by hfs_btree_init() for error cleanup and by hfs_btree_free(). * Input Variable(s): * struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in * the linked list to be deleted. * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev' * field of NULL. * Postconditions: * 'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers * are deleted, freeing the associated memory and hfs_buffer_put()ing * the associated buffer. */ static void hfs_bnode_ditch(struct hfs_bnode *bn) { struct hfs_bnode *tmp; #if defined(DEBUG_BNODES) || defined(DEBUG_ALL) extern int bnode_count; #endif while (bn != NULL) { tmp = bn->next; #if defined(DEBUG_BNODES) || defined(DEBUG_ALL) hfs_warn("deleting node %d from tree %d with count %d\n", bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count); --bnode_count; #endif hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */ /* free all but the header */ if (bn->node) { HFS_DELETE(bn); } bn = tmp; } }
/* * parse_options() * * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger * This function is called by hfs_read_super() to parse the mount options. */ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) { char *this_char, *value; char names, fork; /* initialize the sb with defaults */ memset(hsb, 0, sizeof(*hsb)); hsb->magic = HFS_SB_MAGIC; hsb->s_uid = current->uid; hsb->s_gid = current->gid; hsb->s_umask = current->fs->umask; hsb->s_type = 0x3f3f3f3f; /* == '????' */ hsb->s_creator = 0x3f3f3f3f; /* == '????' */ hsb->s_lowercase = 0; hsb->s_quiet = 0; hsb->s_afpd = 0; /* default version. 0 just selects the defaults */ hsb->s_version = 0; hsb->s_conv = 'b'; names = '?'; fork = '?'; *part = 0; if (!options) { goto done; } for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { *value++ = 0; } /* Numeric-valued options */ if (!strcmp(this_char, "version")) { if (!value || !*value) { return 0; } hsb->s_version = simple_strtoul(value,&value,0); if (*value) { return 0; } } else if (!strcmp(this_char,"uid")) { if (!value || !*value) { return 0; } hsb->s_uid = simple_strtoul(value,&value,0); if (*value) { return 0; } } else if (!strcmp(this_char,"gid")) { if (!value || !*value) { return 0; } hsb->s_gid = simple_strtoul(value,&value,0); if (*value) { return 0; } } else if (!strcmp(this_char,"umask")) { if (!value || !*value) { return 0; } hsb->s_umask = simple_strtoul(value,&value,8); if (*value) { return 0; } } else if (!strcmp(this_char,"part")) { if (!value || !*value) { return 0; } *part = simple_strtoul(value,&value,0); if (*value) { return 0; } /* String-valued options */ } else if (!strcmp(this_char,"type") && value) { if (strlen(value) != 4) { return 0; } hsb->s_type = hfs_get_nl(value); } else if (!strcmp(this_char,"creator") && value) { if (strlen(value) != 4) { return 0; } hsb->s_creator = hfs_get_nl(value); /* Boolean-valued options */ } else if (!strcmp(this_char,"quiet")) { if (value) { return 0; } hsb->s_quiet = 1; } else if (!strcmp(this_char,"afpd")) { if (value) { return 0; } hsb->s_afpd = 1; /* Multiple choice options */ } else if (!strcmp(this_char,"names") && value) { if ((*value && !value[1] && strchr("ntal78c",*value)) || !strcmp(value,"netatalk") || !strcmp(value,"trivial") || !strcmp(value,"alpha") || !strcmp(value,"latin") || !strcmp(value,"7bit") || !strcmp(value,"8bit") || !strcmp(value,"cap")) { names = *value; } else { return 0; } } else if (!strcmp(this_char,"fork") && value) { if ((*value && !value[1] && strchr("nsdc",*value)) || !strcmp(value,"netatalk") || !strcmp(value,"single") || !strcmp(value,"double") || !strcmp(value,"cap")) { fork = *value; } else { return 0; } } else if (!strcmp(this_char,"case") && value) { if ((*value && !value[1] && strchr("la",*value)) || !strcmp(value,"lower") || !strcmp(value,"asis")) { hsb->s_lowercase = (*value == 'l'); } else { return 0; } } else if (!strcmp(this_char,"conv") && value) { if ((*value && !value[1] && strchr("bta",*value)) || !strcmp(value,"binary") || !strcmp(value,"text") || !strcmp(value,"auto")) { hsb->s_conv = *value; } else { return 0; } } else { return 0; } } done: /* Parse the "fork" and "names" options */ if (fork == '?') { fork = hsb->s_afpd ? 'n' : 'c'; } switch (fork) { default: case 'c': hsb->s_ifill = hfs_cap_ifill; hsb->s_reserved1 = hfs_cap_reserved1; hsb->s_reserved2 = hfs_cap_reserved2; break; case 's': hfs_warn("hfs_fs: AppleSingle not yet implemented.\n"); return 0; /* break; */ case 'd': hsb->s_ifill = hfs_dbl_ifill; hsb->s_reserved1 = hfs_dbl_reserved1; hsb->s_reserved2 = hfs_dbl_reserved2; break; case 'n': hsb->s_ifill = hfs_nat_ifill; hsb->s_reserved1 = hfs_nat_reserved1; hsb->s_reserved2 = hfs_nat_reserved2; break; } if (names == '?') { names = fork; } switch (names) { default: case 'n': hsb->s_nameout = hfs_colon2mac; hsb->s_namein = hfs_mac2nat; break; case 'c': hsb->s_nameout = hfs_colon2mac; hsb->s_namein = hfs_mac2cap; break; case 't': hsb->s_nameout = hfs_triv2mac; hsb->s_namein = hfs_mac2triv; break; case '7': hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2seven; break; case '8': hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2eight; break; case 'l': hsb->s_nameout = hfs_latin2mac; hsb->s_namein = hfs_mac2latin; break; case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */ case 's': /* since 'alpha' is the default if fork=s or fork=d. */ case 'd': /* (It is also helpful for poor typists!) */ hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2alpha; break; } return 1; }
/* * hfs_part_find() * * Parse the partition map looking for the * start and length of the 'part'th HFS partition. */ int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent, hfs_s32 *size, hfs_s32 *start) { hfs_buffer buf; hfs_u16 sig; int dd_found = 0; int retval = 1; /* Read block 0 to see if this media is partitioned */ buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1); if (!hfs_buffer_ok(buf)) { hfs_warn("hfs_fs: Unable to read block 0.\n"); goto done; } sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig); hfs_buffer_put(buf); if (sig == htons(HFS_DRVR_DESC_MAGIC)) { /* We are definitely on partitioned media. */ dd_found = 1; } buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1); if (!hfs_buffer_ok(buf)) { hfs_warn("hfs_fs: Unable to read block 1.\n"); goto done; } *size = *start = 0; switch (hfs_get_ns(hfs_buffer_data(buf))) { case __constant_htons(HFS_OLD_PMAP_MAGIC): retval = parse_old_part_table(sys_mdb, buf, part, size, start); break; case __constant_htons(HFS_NEW_PMAP_MAGIC): retval = parse_new_part_table(sys_mdb, buf, part, size, start); break; default: if (dd_found) { /* The media claimed to have a partition map */ if (!silent) { hfs_warn("hfs_fs: This disk has an " "unrecognized partition map type.\n"); } } else { /* Conclude that the media is not partitioned */ retval = 0; } goto done; } if (!retval) { if (*start == 0) { if (part) { hfs_warn("hfs_fs: unable to locate " "HFS partition number %d.\n", part); } else { hfs_warn("hfs_fs: unable to locate any " "HFS partitions.\n"); } retval = 1; } else if (*size < 0) { hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n"); retval = 1; } else if (*start < 0) { hfs_warn("hfs_fs: Partition begins beyond 1 " "Terabyte.\n"); retval = 1; } } done: return retval; }
/* * hfs_btree_init() * * Description: * Given some vital information from the MDB (HFS superblock), * initializes the fields of a (struct hfs_btree). * Input Variable(s): * struct hfs_mdb *mdb: pointer to the MDB * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree * hfs_u32 tsize: the size, in bytes, of the B-tree * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree * Output Variable(s): * NONE * Returns: * (struct hfs_btree *): pointer to the initialized hfs_btree on success, * or NULL on failure * Preconditions: * 'mdb' points to a "valid" (struct hfs_mdb) * Postconditions: * Assuming the inputs are what they claim to be, no errors occur * reading from disk, and no inconsistencies are noticed in the data * read from disk, the return value is a pointer to a "valid" * (struct hfs_btree). If there are errors reading from disk or * inconsistencies are noticed in the data read from disk, then and * all resources that were allocated are released and NULL is * returned. If the inputs are not what they claim to be or if they * are unnoticed inconsistencies in the data read from disk then the * returned hfs_btree is probably going to lead to errors when it is * used in a non-trivial way. */ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid, hfs_byte_t ext[12], hfs_u32 tsize, hfs_u32 csize) { struct hfs_btree * bt; struct BTHdrRec * th; struct hfs_bnode * tmp; unsigned int next; #if defined(DEBUG_HEADER) || defined(DEBUG_ALL) unsigned char *p, *q; #endif if (!mdb || !ext || !HFS_NEW(bt)) { goto bail3; } bt->magic = HFS_BTREE_MAGIC; bt->sys_mdb = mdb->sys_mdb; bt->reserved = 0; bt->lock = 0; hfs_init_waitqueue(&bt->wait); bt->dirt = 0; memset(bt->cache, 0, sizeof(bt->cache)); #if 0 /* this is a fake entry. so we don't need to initialize it. */ memset(&bt->entry, 0, sizeof(bt->entry)); hfs_init_waitqueue(&bt->entry.wait); INIT_LIST_HEAD(&bt->entry.hash); INIT_LIST_HEAD(&bt->entry.list); #endif bt->entry.mdb = mdb; bt->entry.cnid = cnid; bt->entry.type = HFS_CDR_FIL; bt->entry.u.file.magic = HFS_FILE_MAGIC; bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz) >> HFS_SECTOR_SIZE_BITS; bt->entry.u.file.data_fork.entry = &bt->entry; bt->entry.u.file.data_fork.lsize = tsize; bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS; bt->entry.u.file.data_fork.fork = HFS_FK_DATA; hfs_extent_in(&bt->entry.u.file.data_fork, ext); hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY); if (!hfs_buffer_ok(bt->head.buf)) { goto bail2; } th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) + sizeof(struct NodeDescriptor)); /* read in the bitmap nodes (if any) */ tmp = &bt->head; while ((next = tmp->ndFLink)) { if (!HFS_NEW(tmp->next)) { goto bail2; } hfs_bnode_read(tmp->next, bt, next, HFS_STICKY); if (!hfs_buffer_ok(tmp->next->buf)) { goto bail2; } tmp->next->prev = tmp; tmp = tmp->next; } if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) { hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n"); goto bail2; } if (cnid == htonl(HFS_CAT_CNID)) { bt->compare = (hfs_cmpfn)hfs_cat_compare; } else if (cnid == htonl(HFS_EXT_CNID)) { bt->compare = (hfs_cmpfn)hfs_ext_compare; } else { goto bail2; } bt->bthDepth = hfs_get_hs(th->bthDepth); bt->bthRoot = hfs_get_hl(th->bthRoot); bt->bthNRecs = hfs_get_hl(th->bthNRecs); bt->bthFNode = hfs_get_hl(th->bthFNode); bt->bthLNode = hfs_get_hl(th->bthLNode); bt->bthNNodes = hfs_get_hl(th->bthNNodes); bt->bthFree = hfs_get_hl(th->bthFree); bt->bthKeyLen = hfs_get_hs(th->bthKeyLen); #if defined(DEBUG_HEADER) || defined(DEBUG_ALL) hfs_warn("bthDepth %d\n", bt->bthDepth); hfs_warn("bthRoot %d\n", bt->bthRoot); hfs_warn("bthNRecs %d\n", bt->bthNRecs); hfs_warn("bthFNode %d\n", bt->bthFNode); hfs_warn("bthLNode %d\n", bt->bthLNode); hfs_warn("bthKeyLen %d\n", bt->bthKeyLen); hfs_warn("bthNNodes %d\n", bt->bthNNodes); hfs_warn("bthFree %d\n", bt->bthFree); p = (unsigned char *)hfs_buffer_data(bt->head.buf); q = p + HFS_SECTOR_SIZE; while (p < q) { hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++); } #endif /* Read in the root if it exists. The header always exists, but the root exists only if the tree is non-empty */ if (bt->bthDepth && bt->bthRoot) { if (!HFS_NEW(bt->root)) { goto bail2; } hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY); if (!hfs_buffer_ok(bt->root->buf)) { goto bail1; } } else { bt->root = NULL; } return bt; bail1: hfs_bnode_ditch(bt->root); bail2: hfs_bnode_ditch(&bt->head); HFS_DELETE(bt); bail3: return NULL; }
/* * hfs_mdb_get() * * Build the in-core MDB for a filesystem, including * the B-trees and the volume bitmap. */ int hfs_mdb_get(struct super_block *sb) { struct buffer_head *bh; struct hfs_mdb *mdb, *mdb2; unsigned int block; char *ptr; int off2, len, size, sect; sector_t part_start, part_size; loff_t off; __be16 attrib; /* set the device driver to 512-byte blocks */ size = sb_min_blocksize(sb, HFS_SECTOR_SIZE); if (!size) return -EINVAL; if (hfs_get_last_session(sb, &part_start, &part_size)) return -EINVAL; while (1) { /* See if this is an HFS filesystem */ bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); if (!bh) goto out; if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) break; brelse(bh); /* check for a partition block * (should do this only for cdrom/loop though) */ if (hfs_part_find(sb, &part_start, &part_size)) goto out; } HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz); if (!size || (size & (HFS_SECTOR_SIZE - 1))) { hfs_warn("hfs_fs: bad allocation block size %d\n", size); goto out_bh; } size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE); /* size must be a multiple of 512 */ while (size & (size - 1)) size -= HFS_SECTOR_SIZE; sect = be16_to_cpu(mdb->drAlBlSt) + part_start; /* align block size to first sector */ while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS)) size >>= 1; /* align block size to weird alloc size */ while (HFS_SB(sb)->alloc_blksz & (size - 1)) size >>= 1; brelse(bh); if (!sb_set_blocksize(sb, size)) { printk("hfs_fs: unable to set blocksize to %u\n", size); goto out; } bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); if (!bh) goto out; if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) goto out_bh; HFS_SB(sb)->mdb_bh = bh; HFS_SB(sb)->mdb = mdb; /* These parameters are read from the MDB, and never written */ HFS_SB(sb)->part_start = part_start; HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks); HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits; HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) / HFS_SB(sb)->alloc_blksz; if (!HFS_SB(sb)->clumpablks) HFS_SB(sb)->clumpablks = 1; HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >> (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS); /* These parameters are read from and written to the MDB */ HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks); HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID); HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls); HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs); HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt); HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt); /* TRY to get the alternate (backup) MDB. */ sect = part_start + part_size - 2; bh = sb_bread512(sb, sect, mdb2); if (bh) { if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) { HFS_SB(sb)->alt_mdb_bh = bh; HFS_SB(sb)->alt_mdb = mdb2; } else brelse(bh); } if (!HFS_SB(sb)->alt_mdb) { hfs_warn("hfs_fs: unable to locate alternate MDB\n"); hfs_warn("hfs_fs: continuing without an alternate MDB\n"); } HFS_SB(sb)->bitmap = (__be32 *)__get_free_pages(GFP_KERNEL, PAGE_SIZE < 8192 ? 1 : 0); if (!HFS_SB(sb)->bitmap) goto out; /* read in the bitmap */ block = be16_to_cpu(mdb->drVBMSt) + part_start; off = (loff_t)block << HFS_SECTOR_SIZE_BITS; size = (HFS_SB(sb)->fs_ablocks + 8) / 8; ptr = (u8 *)HFS_SB(sb)->bitmap; while (size) { bh = sb_bread(sb, off >> sb->s_blocksize_bits); if (!bh) { hfs_warn("hfs_fs: unable to read volume bitmap\n"); goto out; } off2 = off & (sb->s_blocksize - 1); len = min((int)sb->s_blocksize - off2, size); memcpy(ptr, bh->b_data + off2, len); brelse(bh); ptr += len; off += len; size -= len; } HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp); if (!HFS_SB(sb)->ext_tree) { hfs_warn("hfs_fs: unable to open extent tree\n"); goto out; } HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp); if (!HFS_SB(sb)->cat_tree) { hfs_warn("hfs_fs: unable to open catalog tree\n"); goto out; } attrib = mdb->drAtrb; if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT)) || (attrib & cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT))) { hfs_warn("HFS-fs warning: Filesystem was not cleanly unmounted, " "running fsck.hfs is recommended. mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) { hfs_warn("HFS-fs: Filesystem is marked locked, mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } if (!(sb->s_flags & MS_RDONLY)) { /* Mark the volume uncleanly unmounted in case we crash */ mdb->drAtrb = attrib & cpu_to_be16(~HFS_SB_ATTRIB_UNMNT); mdb->drAtrb = attrib | cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT); mdb->drWrCnt = cpu_to_be32(be32_to_cpu(mdb->drWrCnt) + 1); mdb->drLsMod = hfs_mtime(); mark_buffer_dirty(HFS_SB(sb)->mdb_bh); hfs_buffer_sync(HFS_SB(sb)->mdb_bh); } return 0; out_bh: brelse(bh); out: hfs_mdb_put(sb); return -EIO; }
/* * hfs_mdb_commit() * * Description: * This updates the MDB on disk (look also at hfs_write_super()). * It does not check, if the superblock has been modified, or * if the filesystem has been mounted read-only. It is mainly * called by hfs_write_super() and hfs_btree_extend(). * Input Variable(s): * struct hfs_mdb *mdb: Pointer to the hfs MDB * int backup; * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'mdb' points to a "valid" (struct hfs_mdb). * Postconditions: * The HFS MDB and on disk will be updated, by copying the possibly * modified fields from the in memory MDB (in native byte order) to * the disk block buffer. * If 'backup' is non-zero then the alternate MDB is also written * and the function doesn't return until it is actually on disk. */ void hfs_mdb_commit(struct super_block *sb) { struct hfs_mdb *mdb = HFS_SB(sb)->mdb; if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) { /* These parameters may have been modified, so write them back */ mdb->drLsMod = hfs_mtime(); mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks); mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id); mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files); mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs); mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count); mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count); /* write MDB to disk */ mark_buffer_dirty(HFS_SB(sb)->mdb_bh); } /* write the backup MDB, not returning until it is written. * we only do this when either the catalog or extents overflow * files grow. */ if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) && HFS_SB(sb)->alt_mdb) { hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec, &mdb->drXTFlSize, NULL); hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec, &mdb->drCTFlSize, NULL); memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE); HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh); hfs_buffer_sync(HFS_SB(sb)->alt_mdb_bh); } if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) { struct buffer_head *bh; sector_t block; char *ptr; int off, size, len; block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start; off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1); block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS; size = (HFS_SB(sb)->fs_ablocks + 7) / 8; ptr = (u8 *)HFS_SB(sb)->bitmap; while (size) { bh = sb_bread(sb, block); if (!bh) { hfs_warn("hfs_fs: unable to read volume bitmap\n"); break; } len = min((int)sb->s_blocksize - off, size); memcpy(bh->b_data + off, ptr, len); mark_buffer_dirty(bh); brelse(bh); block++; off = 0; ptr += len; size -= len; } } }
/* * hfs_read_super() * * This is the function that is responsible for mounting an HFS * filesystem. It performs all the tasks necessary to get enough data * from the disk to read the root inode. This includes parsing the * mount options, dealing with Macintosh partitions, reading the * superblock and the allocation bitmap blocks, calling * hfs_btree_init() to get the necessary data about the extents and * catalog B-trees and, finally, reading the root inode into memory. */ struct super_block *hfs_read_super(struct super_block *s, void *data, int silent) { struct hfs_mdb *mdb; struct hfs_cat_key key; kdev_t dev = s->s_dev; hfs_s32 part_size, part_start; struct inode *root_inode; int part; if (!parse_options((char *)data, HFS_SB(s), &part)) { hfs_warn("hfs_fs: unable to parse mount options.\n"); goto bail3; } /* set the device driver to 512-byte blocks */ set_blocksize(dev, HFS_SECTOR_SIZE); s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS; s->s_blocksize = HFS_SECTOR_SIZE; #ifdef CONFIG_MAC_PARTITION /* check to see if we're in a partition */ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); /* erk. try parsing the partition table ourselves */ if (!mdb) { if (hfs_part_find(s, part, silent, &part_size, &part_start)) { goto bail2; } mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); } #else if (hfs_part_find(s, part, silent, &part_size, &part_start)) { goto bail2; } mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); #endif if (!mdb) { if (!silent) { hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", kdevname(dev)); } goto bail2; } HFS_SB(s)->s_mdb = mdb; if (HFS_ITYPE(mdb->next_id) != 0) { hfs_warn("hfs_fs: too many files.\n"); goto bail1; } s->s_magic = HFS_SUPER_MAGIC; s->s_op = &hfs_super_operations; /* try to get the root inode */ hfs_cat_build_key(htonl(HFS_POR_CNID), (struct hfs_name *)(mdb->vname), &key); root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL); if (!root_inode) goto bail_no_root; s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto bail_no_root; /* fix up pointers. */ HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = s->s_root; s->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ return s; bail_no_root: hfs_warn("hfs_fs: get root inode failed.\n"); iput(root_inode); bail1: hfs_mdb_put(mdb, s->s_flags & MS_RDONLY); bail2: set_blocksize(dev, BLOCK_SIZE); bail3: return NULL; }
/* * bdelete() * * Delete the given record from a B-tree. */ static int bdelete(struct hfs_brec *brec) { struct hfs_btree *tree = brec->tree; struct hfs_belem *belem = brec->bottom; struct hfs_belem *parent = (belem-1); struct hfs_bnode *bnode; hfs_u32 left_node, right_node; struct hfs_bnode_ref left, right; int left_space, right_space, min_space; int fix_right_key; int fix_key; while ((belem > brec->top) && (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) { bnode = belem->bnr.bn; fix_key = belem->flags & HFS_BPATH_FIRST; fix_right_key = 0; bdelete_nonempty(brec, belem); if (bnode->node == tree->root->node) { del_root(&belem->bnr); --brec->bottom; goto done; } /* check for btree corruption which could lead to deadlock */ left_node = bnode->ndBLink; right_node = bnode->ndFLink; if ((left_node && hfs_bnode_in_brec(left_node, brec)) || (right_node && hfs_bnode_in_brec(right_node, brec)) || (left_node == right_node)) { hfs_warn("hfs_bdelete: corrupt btree\n"); hfs_brec_relse(brec, NULL); return -EIO; } /* grab the left neighbor if it exists */ if (left_node) { hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV); left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE); if (!left.bn) { hfs_warn("hfs_bdelete: unable to read left " "neighbor.\n"); hfs_brec_relse(brec, NULL); return -EIO; } hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE); if (parent->record != 1) { left_space = bnode_freespace(left.bn); } else { left_space = NO_SPACE; } } else { left.bn = NULL; left_space = NO_SPACE; } /* grab the right neighbor if it exists */ if (right_node) { right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE); if (!right.bn) { hfs_warn("hfs_bdelete: unable to read right " "neighbor.\n"); hfs_bnode_relse(&left); hfs_brec_relse(brec, NULL); return -EIO; } if (parent->record < parent->bnr.bn->ndNRecs) { right_space = bnode_freespace(right.bn); } else { right_space = NO_SPACE; } } else { right.bn = NULL; right_space = NO_SPACE; } if (left_space < right_space) { min_space = left_space; } else { min_space = right_space; } if (min_space == NO_SPACE) { hfs_warn("hfs_bdelete: no siblings?\n"); hfs_brec_relse(brec, NULL); return -EIO; } if (bnode->ndNRecs == 0) { delete_empty_bnode(left_node, &left, &belem->bnr, right_node, &right); } else if (min_space + bnode_freespace(bnode) >= FULL) { if ((right_space == NO_SPACE) || ((right_space == min_space) && (left_space != NO_SPACE))) { hfs_bnode_shift_left(left.bn, bnode, bnode->ndNRecs); } else { hfs_bnode_shift_right(bnode, right.bn, 1); fix_right_key = 1; } delete_empty_bnode(left_node, &left, &belem->bnr, right_node, &right); } else if (min_space == right_space) { balance(bnode, right.bn); fix_right_key = 1; } else { balance(left.bn, bnode); fix_key = 1; } if (fix_right_key) { hfs_bnode_update_key(brec, belem, right.bn, 1); } hfs_bnode_relse(&left); hfs_bnode_relse(&right); if (bnode->ndNRecs) { if (fix_key) { hfs_bnode_update_key(brec, belem, bnode, 0); } goto done; } hfs_bnode_free(&belem->bnr); --brec->bottom; belem = parent; --parent; } if (belem < brec->top) { hfs_warn("hfs_bdelete: Missing parent.\n"); hfs_brec_relse(brec, NULL); return -EIO; } bdelete_nonempty(brec, belem); done: hfs_brec_relse(brec, NULL); return 0; }
/* * hfs_read_super() * * This is the function that is responsible for mounting an HFS * filesystem. It performs all the tasks necessary to get enough data * from the disk to read the root inode. This includes parsing the * mount options, dealing with Macintosh partitions, reading the * superblock and the allocation bitmap blocks, calling * hfs_btree_init() to get the necessary data about the extents and * catalog B-trees and, finally, reading the root inode into memory. */ static int hfs_fill_super(struct super_block *sb, void *data, int silent) { struct hfs_sb_info *sbi; struct hfs_find_data fd; hfs_cat_rec rec; struct inode *root_inode; int res; sbi = kmalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; sb->s_fs_info = sbi; memset(sbi, 0, sizeof(struct hfs_sb_info)); INIT_HLIST_HEAD(&sbi->rsrc_inodes); res = -EINVAL; if (!parse_options((char *)data, sbi)) { hfs_warn("hfs_fs: unable to parse mount options.\n"); goto bail3; } sb->s_op = &hfs_super_operations; sb->s_flags |= MS_NODIRATIME; init_MUTEX(&sbi->bitmap_lock); res = hfs_mdb_get(sb); if (res) { if (!silent) hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", hfs_mdb_name(sb)); res = -EINVAL; goto bail2; } /* try to get the root inode */ hfs_find_init(HFS_SB(sb)->cat_tree, &fd); res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd); if (!res) hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, fd.entrylength); if (res) { hfs_find_exit(&fd); goto bail_no_root; } root_inode = hfs_iget(sb, &fd.search_key->cat, &rec); hfs_find_exit(&fd); if (!root_inode) goto bail_no_root; sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto bail_iput; sb->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ return 0; bail_iput: iput(root_inode); bail_no_root: hfs_warn("hfs_fs: get root inode failed.\n"); hfs_mdb_put(sb); bail2: bail3: kfree(sbi); return res; }
/* * parse_options() * * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger * This function is called by hfs_read_super() to parse the mount options. */ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) { char *p; char names, fork; substring_t args[MAX_OPT_ARGS]; int option; /* initialize the sb with defaults */ memset(hsb, 0, sizeof(*hsb)); hsb->magic = HFS_SB_MAGIC; hsb->s_uid = current->uid; hsb->s_gid = current->gid; hsb->s_umask = current->fs->umask; hsb->s_type = 0x3f3f3f3f; /* == '????' */ hsb->s_creator = 0x3f3f3f3f; /* == '????' */ hsb->s_lowercase = 0; hsb->s_quiet = 0; hsb->s_afpd = 0; /* default version. 0 just selects the defaults */ hsb->s_version = 0; hsb->s_conv = 'b'; names = '?'; fork = '?'; *part = 0; if (!options) { goto done; } while ((p = strsep(&options,",")) != NULL) { int token; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { /* Numeric-valued options */ case Opt_version: if (match_int(&args[0], &option)) return 0; hsb->s_version = option; break; case Opt_uid: if (match_int(&args[0], &option)) return 0; hsb->s_uid = option; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; hsb->s_gid = option; break; case Opt_umask: if (match_octal(&args[0], &option)) return 0; hsb->s_umask = option; break; case Opt_part: if (match_int(&args[0], &option)) return 0; *part = option; break; /* String-valued options */ case Opt_type: if (strlen(args[0].from) != 4) { return 0; } hsb->s_type = hfs_get_nl(args[0].from); break; case Opt_creator: if (strlen(args[0].from) != 4) { return 0; } hsb->s_creator = hfs_get_nl(args[0].from); break; /* Boolean-valued options */ case Opt_quiet: hsb->s_quiet = 1; break; case Opt_afpd: hsb->s_afpd = 1; break; /* Multiple choice options */ case Opt_names_netatalk: names = 'n'; break; case Opt_names_trivial: names = 't'; break; case Opt_names_alpha: names = 'a'; break; case Opt_names_latin: names = 'l'; break; case Opt_names_7bit: names = '7'; break; case Opt_names_8bit: names = '8'; break; case Opt_names_cap: names = 'c'; break; case Opt_fork_netatalk: fork = 'n'; break; case Opt_fork_single: fork = 's'; break; case Opt_fork_double: fork = 'd'; break; case Opt_fork_cap: fork = 'c'; break; case Opt_case_lower: hsb->s_lowercase = 1; break; case Opt_case_asis: hsb->s_lowercase = 0; break; case Opt_conv_binary: hsb->s_conv = 'b'; break; case Opt_conv_text: hsb->s_conv = 't'; break; case Opt_conv_auto: hsb->s_conv = 'a'; break; default: return 0; } } done: /* Parse the "fork" and "names" options */ if (fork == '?') { fork = hsb->s_afpd ? 'n' : 'c'; } switch (fork) { default: case 'c': hsb->s_ifill = hfs_cap_ifill; hsb->s_reserved1 = hfs_cap_reserved1; hsb->s_reserved2 = hfs_cap_reserved2; break; case 's': hfs_warn("hfs_fs: AppleSingle not yet implemented.\n"); return 0; /* break; */ case 'd': hsb->s_ifill = hfs_dbl_ifill; hsb->s_reserved1 = hfs_dbl_reserved1; hsb->s_reserved2 = hfs_dbl_reserved2; break; case 'n': hsb->s_ifill = hfs_nat_ifill; hsb->s_reserved1 = hfs_nat_reserved1; hsb->s_reserved2 = hfs_nat_reserved2; break; } if (names == '?') { names = fork; } switch (names) { default: case 'n': hsb->s_nameout = hfs_colon2mac; hsb->s_namein = hfs_mac2nat; break; case 'c': hsb->s_nameout = hfs_colon2mac; hsb->s_namein = hfs_mac2cap; break; case 't': hsb->s_nameout = hfs_triv2mac; hsb->s_namein = hfs_mac2triv; break; case '7': hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2seven; break; case '8': hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2eight; break; case 'l': hsb->s_nameout = hfs_latin2mac; hsb->s_namein = hfs_mac2latin; break; case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */ case 's': /* since 'alpha' is the default if fork=s or fork=d. */ case 'd': /* (It is also helpful for poor typists!) */ hsb->s_nameout = hfs_prcnt2mac; hsb->s_namein = hfs_mac2alpha; break; } return 1; }
/* * del_root() * * Description: * Delete the current root bnode. * Input Variable(s): * struct hfs_bnode_ref *root: reference to the root bnode * Output Variable(s): * NONE * Returns: * int: 0 on success, error code on failure * Preconditions: * 'root' refers to the root bnode with HFS_LOCK_WRITE access. * None of 'root's children are held with HFS_LOCK_WRITE access. * Postconditions: * The current 'root' node is removed from the tree and the depth * of the tree is reduced by one. * If 'root' is an index node with exactly one child, then that * child becomes the new root of the tree. * If 'root' is an empty leaf node the tree becomes empty. * Upon return access to 'root' is relinquished. */ static int del_root(struct hfs_bnode_ref *root) { struct hfs_btree *tree = root->bn->tree; struct hfs_bnode_ref child; hfs_u32 node; if (root->bn->ndNRecs > 1) { return 0; } else if (root->bn->ndNRecs == 0) { /* tree is empty */ tree->bthRoot = 0; tree->root = NULL; tree->bthRoot = 0; tree->bthFNode = 0; tree->bthLNode = 0; --tree->bthDepth; tree->dirt = 1; if (tree->bthDepth) { hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n", tree->bthDepth); goto bail; } return hfs_bnode_free(root); } else if (root->bn->ndType == ndIndxNode) { /* tree is non-empty */ node = hfs_get_hl(bkey_record(bnode_datastart(root->bn))); child = hfs_bnode_find(tree, node, HFS_LOCK_READ); if (!child.bn) { hfs_warn("hfs_bdelete: can't read child node.\n"); goto bail; } child.bn->sticky = HFS_STICKY; if (child.bn->next) { child.bn->next->prev = child.bn->prev; } if (child.bn->prev) { child.bn->prev->next = child.bn->next; } if (bhash(tree, child.bn->node) == child.bn) { bhash(tree, child.bn->node) = child.bn->next; } child.bn->next = NULL; child.bn->prev = NULL; tree->bthRoot = child.bn->node; tree->root = child.bn; /* re-assign bthFNode and bthLNode if the new root is a leaf node. */ if (child.bn->ndType == ndLeafNode) { tree->bthFNode = node; tree->bthLNode = node; } hfs_bnode_relse(&child); tree->bthRoot = node; --tree->bthDepth; tree->dirt = 1; if (!tree->bthDepth) { hfs_warn("hfs_bdelete: non-empty tree with " "bthDepth == 0\n"); goto bail; } return hfs_bnode_free(root); /* marks tree dirty */ } hfs_bnode_relse(root); return 0; bail: hfs_bnode_relse(root); return -EIO; }