Пример #1
0
/*
 * 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
		}
	}
}
Пример #2
0
/*
 * 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;
}
Пример #3
0
/*
 * 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);
}
Пример #4
0
/*
 * 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);
		}
Пример #5
0
/*
 * 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;
}
Пример #6
0
/*
 * 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;
}
Пример #7
0
/*
 * 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;
}
Пример #8
0
/*
 * 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;
	}
}
Пример #9
0
/*
 * 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;
}
Пример #10
0
/*
 * 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;
}
Пример #11
0
/*
 * 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;
}
Пример #12
0
/*
 * 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;
}
Пример #13
0
/*
 * 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;
		}
	}
}
Пример #14
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.
 */
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;	
}
Пример #15
0
/*
 * 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;
}
Пример #16
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;
}
Пример #17
0
/*
 * 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;
}
Пример #18
0
/*
 * 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;
}