Exemple #1
0
/*
 * hfs_lookup()
 */
static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
				 struct nameidata *nd)
{
	hfs_cat_rec rec;
	struct hfs_find_data fd;
	struct inode *inode = NULL;
	int res;

	hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
	res = hfs_brec_read(&fd, &rec, sizeof(rec));
	if (res) {
		hfs_find_exit(&fd);
		if (res == -ENOENT) {
			/* No such entry */
			inode = NULL;
			goto done;
		}
		return ERR_PTR(res);
	}
	inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec);
	hfs_find_exit(&fd);
	if (!inode)
		return ERR_PTR(-EACCES);
done:
	d_add(dentry, inode);
	return NULL;
}
Exemple #2
0
/*
 * hfs_rename()
 *
 * This is the rename() entry in the inode_operations structure for
 * regular HFS directories.  The purpose is to rename an existing
 * file or directory, given the inode for the current directory and
 * the name (and its length) of the existing file/directory and the
 * inode for the new directory and the name (and its length) of the
 * new file/directory.
 * XXX: how do you handle must_be dir?
 */
static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		      struct inode *new_dir, struct dentry *new_dentry,
		      unsigned int flags)
{
	int res;

	if (flags & ~RENAME_NOREPLACE)
		return -EINVAL;

	/* Unlink destination if it already exists */
	if (d_really_is_positive(new_dentry)) {
		res = hfs_remove(new_dir, new_dentry);
		if (res)
			return res;
	}

	res = hfs_cat_move(d_inode(old_dentry)->i_ino,
			   old_dir, &old_dentry->d_name,
			   new_dir, &new_dentry->d_name);
	if (!res)
		hfs_cat_build_key(old_dir->i_sb,
				  (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key,
				  new_dir->i_ino, &new_dentry->d_name);
	return res;
}
Exemple #3
0
/*
 * build_key()
 *
 * Build a key for a file by the given name in the given directory.
 * If the name matches one of the reserved names returns 1 otherwise 0.
 */
static int build_key(struct hfs_cat_key *key, struct inode *dir,
		     const char *name, int len)
{
	struct hfs_name cname;
	const struct hfs_name *reserved;

	/* mangle the name */
	hfs_nameout(dir, &cname, name, len);

	/* check against reserved names */
	reserved = HFS_SB(dir->i_sb)->s_reserved1;
	while (reserved->Len) {
		if (hfs_streq(reserved, &cname)) {
			return 1;
		}
		++reserved;
	}

	/* check against the names reserved only in the root directory */
	if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
		reserved = HFS_SB(dir->i_sb)->s_reserved2;
		while (reserved->Len) {
			if (hfs_streq(reserved, &cname)) {
				return 1;
			}
			++reserved;
		}
	}

	/* build the key */
	hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);

	return 0;
}
Exemple #4
0
/*
 * nat_hdr_unlink()
 *
 * This is the unlink() entry in the inode_operations structure for
 * Netatalk .AppleDouble directories.  The purpose is to delete an
 * existing file, given the inode for the parent directory and the name
 * (and its length) of the existing file.
 *
 * WE DON'T ACTUALLY DELETE HEADER THE FILE.
 * In non-afpd-compatible mode:
 *   We return -EPERM.
 * In afpd-compatible mode:
 *   We return success if the file exists or is .Parent.
 *   Otherwise we return -ENOENT.
 */
static int nat_hdr_unlink(struct inode *dir, const char *name, int len)
{
	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
	int error = 0;

	if (!HFS_SB(dir->i_sb)->s_afpd) {
		/* Not in AFPD compatibility mode */
		error = -EPERM;
	} else {
		struct hfs_name cname;

		hfs_nameout(dir, &cname, name, len);
		if (!hfs_streq(&cname, DOT_PARENT)) {
			struct hfs_cat_entry *victim;
			struct hfs_cat_key key;

			hfs_cat_build_key(entry->cnid, &cname, &key);
			victim = hfs_cat_get(entry->mdb, &key);

			if (victim) {
				/* pretend to succeed */
				hfs_cat_put(victim);
			} else {
				error = -ENOENT;
			}
		}
	}
	iput(dir);
	return error;
}
Exemple #5
0
/*
 * cap_lookup()
 *
 * This is the lookup() entry in the inode_operations structure for
 * HFS directories in the CAP scheme.  The purpose is to generate the
 * inode corresponding to an entry in a directory, given the inode for
 * the directory and the name (and its length) of the entry.
 */
static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry)
{
	ino_t dtype;
	struct hfs_name cname;
	struct hfs_cat_entry *entry;
	struct hfs_cat_key key;
	struct inode *inode = NULL;

	dentry->d_op = &hfs_dentry_operations;
	entry = HFS_I(dir)->entry;
	dtype = HFS_ITYPE(dir->i_ino);

	/* Perform name-mangling */
	hfs_nameout(dir, &cname, dentry->d_name.name, 
		    dentry->d_name.len);

	/* no need to check for "."  or ".." */

	/* Check for special directories if in a normal directory.
	   Note that cap_dupdir() does an iput(dir). */
	if (dtype==HFS_CAP_NDIR) {
		/* Check for ".resource", ".finderinfo" and ".rootinfo" */
		if (hfs_streq(cname.Name, cname.Len, 
			      DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) {
			++entry->count; /* __hfs_iget() eats one */
			inode = hfs_iget(entry, HFS_CAP_RDIR, dentry);
			goto done;
		} else if (hfs_streq(cname.Name, cname.Len, 
				     DOT_FINDERINFO->Name, 
				     DOT_FINDERINFO_LEN)) {
			++entry->count; /* __hfs_iget() eats one */
			inode = hfs_iget(entry, HFS_CAP_FDIR, dentry);
			goto done;
		} else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
			   hfs_streq(cname.Name, cname.Len, 
				     DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) {
			++entry->count; /* __hfs_iget() eats one */
			inode = hfs_iget(entry, HFS_CAP_FNDR, dentry);
			goto done;
		}
	}

	/* Do an hfs_iget() on the mangled name. */
	hfs_cat_build_key(entry->cnid, &cname, &key);
	inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
			 HFS_I(dir)->file_type, dentry);

	/* Don't return a resource fork for a directory */
	if (inode && (dtype == HFS_CAP_RDIR) && 
	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
	        iput(inode); /* this does an hfs_cat_put */
		inode = NULL;
	}

done:
	d_add(dentry, inode);
	return NULL;
}
Exemple #6
0
/*
 * hfs_rename()
 *
 * This is the rename() entry in the inode_operations structure for
 * regular HFS directories.  The purpose is to rename an existing
 * file or directory, given the inode for the current directory and
 * the name (and its length) of the existing file/directory and the
 * inode for the new directory and the name (and its length) of the
 * new file/directory.
 * XXX: how do you handle must_be dir?
 */
int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	       struct inode *new_dir, struct dentry *new_dentry)
{
	int res;

	/* Unlink destination if it already exists */
	if (new_dentry->d_inode) {
		res = hfs_unlink(new_dir, new_dentry);
		if (res)
			return res;
	}

	res = hfs_cat_move(old_dentry->d_inode->i_ino,
			   old_dir, &old_dentry->d_name,
			   new_dir, &new_dentry->d_name);
	if (!res)
		hfs_cat_build_key((btree_key *)&HFS_I(old_dentry->d_inode)->cat_key,
				  new_dir->i_ino, &new_dentry->d_name);
	return res;
}
static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		      struct inode *new_dir, struct dentry *new_dentry)
{
	int res;

	
	if (new_dentry->d_inode) {
		res = hfs_remove(new_dir, new_dentry);
		if (res)
			return res;
	}

	res = hfs_cat_move(old_dentry->d_inode->i_ino,
			   old_dir, &old_dentry->d_name,
			   new_dir, &new_dentry->d_name);
	if (!res)
		hfs_cat_build_key(old_dir->i_sb,
				  (btree_key *)&HFS_I(old_dentry->d_inode)->cat_key,
				  new_dir->i_ino, &new_dentry->d_name);
	return res;
}
Exemple #8
0
/*
 * nat_hdr_rename()
 *
 * This is the rename() entry in the inode_operations structure for
 * Netatalk header directories.  The purpose is to rename an existing
 * file given the inode for the current directory and the name 
 * (and its length) of the existing file and the inode for the new
 * directory and the name (and its length) of the new file/directory.
 *
 * WE NEVER MOVE ANYTHING.
 * In non-afpd-compatible mode:
 *   We return -EPERM.
 * In afpd-compatible mode:
 *   If the source header doesn't exist, we return -ENOENT.
 *   If the destination is not a header directory we return -EPERM.
 *   We return success if the destination is also a header directory
 *    and the header exists or is ".Parent".
 */
static int nat_hdr_rename(struct inode *old_dir, const char *old_name,
			  int old_len, struct inode *new_dir,
			  const char *new_name, int new_len, int must_be_dir)
{
	struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
	int error = 0;

	if (!HFS_SB(old_dir->i_sb)->s_afpd) {
		/* Not in AFPD compatibility mode */
		error = -EPERM;
	} else {
		struct hfs_name cname;

		hfs_nameout(old_dir, &cname, old_name, old_len);
		if (!hfs_streq(&cname, DOT_PARENT)) {
			struct hfs_cat_entry *victim;
			struct hfs_cat_key key;

			hfs_cat_build_key(entry->cnid, &cname, &key);
			victim = hfs_cat_get(entry->mdb, &key);

			if (victim) {
				/* pretend to succeed */
				hfs_cat_put(victim);
			} else {
				error = -ENOENT;
			}
		}

		if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
			error = -EPERM;
		}
	}
	iput(old_dir);
	iput(new_dir);
	return error;
}
Exemple #9
0
/*
 * hfs_lookup()
 */
static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
				 unsigned int flags)
{
	hfs_cat_rec rec;
	struct hfs_find_data fd;
	struct inode *inode = NULL;
	int res;

	res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
	if (res)
		return ERR_PTR(res);
	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
	res = hfs_brec_read(&fd, &rec, sizeof(rec));
	if (res) {
		if (res != -ENOENT)
			inode = ERR_PTR(res);
	} else {
		inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec);
		if (!inode)
			inode = ERR_PTR(-EACCES);
	}
	hfs_find_exit(&fd);
	return d_splice_alias(inode, dentry);
}
Exemple #10
0
/*
 * hfs_readdir
 */
static int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	struct inode *inode = filp->f_path.dentry->d_inode;
	struct super_block *sb = inode->i_sb;
	int len, err;
	char strbuf[HFS_MAX_NAMELEN];
	union hfs_cat_rec entry;
	struct hfs_find_data fd;
	struct hfs_readdir_data *rd;
	u16 type;

	if (filp->f_pos >= inode->i_size)
		return 0;

	hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
	hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
	err = hfs_brec_find(&fd);
	if (err)
		goto out;

	switch ((u32)filp->f_pos) {
	case 0:
		/* This is completely artificial... */
		if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
			goto out;
		filp->f_pos++;
		/* fall through */
	case 1:
		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
			err = -EIO;
			goto out;
		}

		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
		if (entry.type != HFS_CDR_THD) {
			printk(KERN_ERR "hfs: bad catalog folder thread\n");
			err = -EIO;
			goto out;
		}
		//if (fd.entrylength < HFS_MIN_THREAD_SZ) {
		//	printk(KERN_ERR "hfs: truncated catalog thread\n");
		//	err = -EIO;
		//	goto out;
		//}
		if (filldir(dirent, "..", 2, 1,
			    be32_to_cpu(entry.thread.ParID), DT_DIR))
			goto out;
		filp->f_pos++;
		/* fall through */
	default:
		if (filp->f_pos >= inode->i_size)
			goto out;
		err = hfs_brec_goto(&fd, filp->f_pos - 1);
		if (err)
			goto out;
	}

	for (;;) {
		if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) {
			printk(KERN_ERR "hfs: walked past end of dir\n");
			err = -EIO;
			goto out;
		}

		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
			err = -EIO;
			goto out;
		}

		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
		type = entry.type;
		len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
		if (type == HFS_CDR_DIR) {
			if (fd.entrylength < sizeof(struct hfs_cat_dir)) {
				printk(KERN_ERR "hfs: small dir entry\n");
				err = -EIO;
				goto out;
			}
			if (filldir(dirent, strbuf, len, filp->f_pos,
				    be32_to_cpu(entry.dir.DirID), DT_DIR))
				break;
		} else if (type == HFS_CDR_FIL) {
			if (fd.entrylength < sizeof(struct hfs_cat_file)) {
				printk(KERN_ERR "hfs: small file entry\n");
				err = -EIO;
				goto out;
			}
			if (filldir(dirent, strbuf, len, filp->f_pos,
				    be32_to_cpu(entry.file.FlNum), DT_REG))
				break;
		} else {
			printk(KERN_ERR "hfs: bad catalog entry type %d\n", type);
			err = -EIO;
			goto out;
		}
		filp->f_pos++;
		if (filp->f_pos >= inode->i_size)
			goto out;
		err = hfs_brec_goto(&fd, 1);
		if (err)
			goto out;
	}
	rd = filp->private_data;
	if (!rd) {
		rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL);
		if (!rd) {
			err = -ENOMEM;
			goto out;
		}
		filp->private_data = rd;
		rd->file = filp;
		list_add(&rd->list, &HFS_I(inode)->open_dir_list);
	}
	memcpy(&rd->key, &fd.key, sizeof(struct hfs_cat_key));
out:
	hfs_find_exit(&fd);
	return err;
}
Exemple #11
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;	
}
Exemple #12
0
/*
 * hfs_readdir
 */
static int hfs_readdir(struct file *file, struct dir_context *ctx)
{
	struct inode *inode = file_inode(file);
	struct super_block *sb = inode->i_sb;
	int len, err;
	char strbuf[HFS_MAX_NAMELEN];
	union hfs_cat_rec entry;
	struct hfs_find_data fd;
	struct hfs_readdir_data *rd;
	u16 type;

	if (ctx->pos >= inode->i_size)
		return 0;

	err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
	if (err)
		return err;
	hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
	err = hfs_brec_find(&fd);
	if (err)
		goto out;

	if (ctx->pos == 0) {
		/* This is completely artificial... */
		if (!dir_emit_dot(file, ctx))
			goto out;
		ctx->pos = 1;
	}
	if (ctx->pos == 1) {
		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
			err = -EIO;
			goto out;
		}

		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
		if (entry.type != HFS_CDR_THD) {
			pr_err("bad catalog folder thread\n");
			err = -EIO;
			goto out;
		}
		//if (fd.entrylength < HFS_MIN_THREAD_SZ) {
		//	pr_err("truncated catalog thread\n");
		//	err = -EIO;
		//	goto out;
		//}
		if (!dir_emit(ctx, "..", 2,
			    be32_to_cpu(entry.thread.ParID), DT_DIR))
			goto out;
		ctx->pos = 2;
	}
	if (ctx->pos >= inode->i_size)
		goto out;
	err = hfs_brec_goto(&fd, ctx->pos - 1);
	if (err)
		goto out;

	for (;;) {
		if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) {
			pr_err("walked past end of dir\n");
			err = -EIO;
			goto out;
		}

		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
			err = -EIO;
			goto out;
		}

		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
		type = entry.type;
		len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
		if (type == HFS_CDR_DIR) {
			if (fd.entrylength < sizeof(struct hfs_cat_dir)) {
				pr_err("small dir entry\n");
				err = -EIO;
				goto out;
			}
			if (!dir_emit(ctx, strbuf, len,
				    be32_to_cpu(entry.dir.DirID), DT_DIR))
				break;
		} else if (type == HFS_CDR_FIL) {
			if (fd.entrylength < sizeof(struct hfs_cat_file)) {
				pr_err("small file entry\n");
				err = -EIO;
				goto out;
			}
			if (!dir_emit(ctx, strbuf, len,
				    be32_to_cpu(entry.file.FlNum), DT_REG))
				break;
		} else {
			pr_err("bad catalog entry type %d\n", type);
			err = -EIO;
			goto out;
		}
		ctx->pos++;
		if (ctx->pos >= inode->i_size)
			goto out;
		err = hfs_brec_goto(&fd, 1);
		if (err)
			goto out;
	}
	rd = file->private_data;
	if (!rd) {
		rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL);
		if (!rd) {
			err = -ENOMEM;
			goto out;
		}
		file->private_data = rd;
		rd->file = file;
		spin_lock(&HFS_I(inode)->open_dir_lock);
		list_add(&rd->list, &HFS_I(inode)->open_dir_list);
		spin_unlock(&HFS_I(inode)->open_dir_lock);
	}
	/*
	 * Can be done after the list insertion; exclusion with
	 * hfs_delete_cat() is provided by directory lock.
	 */
	memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
out:
	hfs_find_exit(&fd);
	return err;
}
Exemple #13
0
/*
 * nat_lookup()
 *
 * This is the lookup() entry in the inode_operations structure for
 * HFS directories in the Netatalk scheme.  The purpose is to generate
 * the inode corresponding to an entry in a directory, given the inode
 * for the directory and the name (and its length) of the entry.
 */
static int nat_lookup(struct inode * dir, const char * name,
		      int len, struct inode ** result)
{
	ino_t dtype;
	struct hfs_name cname;
	struct hfs_cat_entry *entry;
	struct hfs_cat_key key;
	struct inode *inode = NULL;

	if (!dir || !S_ISDIR(dir->i_mode)) {
		goto done;
	}

	entry = HFS_I(dir)->entry;
	dtype = HFS_ITYPE(dir->i_ino);

	if (len && !name) {
		*result = NULL;
		iput(dir);
		return -EINVAL;
	}

	/* Perform name-mangling */
	hfs_nameout(dir, &cname, name, len);

	/* Check for "." */
	if (hfs_streq(&cname, DOT)) {
		/* this little trick skips the iget and iput */
		*result = dir;
		return 0;
	}

	/* Check for "..". */
	if (hfs_streq(&cname, DOT_DOT)) {
		struct hfs_cat_entry *parent;

		if (dtype != HFS_NAT_NDIR) {
			/* Case for ".." in ".AppleDouble" */
			parent = entry;
			++entry->count; /* __hfs_iget() eats one */
		} else {
			/* Case for ".." in a normal directory */
			parent = hfs_cat_parent(entry);
		}
		inode = __hfs_iget(parent, HFS_NAT_NDIR, 0);
		goto done;
	}

	/* Check for ".AppleDouble" if in a normal directory,
	   and for ".Parent" in ".AppleDouble". */
	if (dtype==HFS_NAT_NDIR) {
		/* Check for ".AppleDouble" */
		if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
			++entry->count; /* __hfs_iget() eats one */
			inode = __hfs_iget(entry, HFS_NAT_HDIR, 1);
			goto done;
		}
	} else if (dtype==HFS_NAT_HDIR) {
		if (hfs_streq(&cname, DOT_PARENT)) {
			++entry->count; /* __hfs_iget() eats one */
			inode = __hfs_iget(entry, HFS_NAT_HDR, 0);
			goto done;
		}
	}

	/* Do an hfs_iget() on the mangled name. */
	hfs_cat_build_key(entry->cnid, &cname, &key);
	inode = hfs_iget(entry->mdb, &key, HFS_I(dir)->file_type);

	/* Don't return a header file for a directory other than .Parent */
	if (inode && (dtype == HFS_NAT_HDIR) &&
	    (HFS_I(inode)->entry != entry) &&
	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
		iput(inode);
		inode = NULL;
	}

done:
	iput(dir);
	*result = inode;
	return inode ? 0 : -ENOENT;
}