/* * 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; }
/* * 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; }
/* * cap_readdir() * * This is the readdir() entry in the file_operations structure for * HFS directories in the CAP scheme. The purpose is to enumerate the * entries in a directory, given the inode of the directory and a * (struct file *), the 'f_pos' field of which indicates the location * in the directory. The (struct file *) is updated so that the next * call with the same 'dir' and 'filp' arguments will produce the next * directory entry. The entries are returned in 'dirent', which is * "filled-in" by calling filldir(). This allows the same readdir() * function be used for different dirent formats. We try to read in * as many entries as we can before filldir() refuses to take any more. * * XXX: In the future it may be a good idea to consider not generating * metadata files for covered directories since the data doesn't * correspond to the mounted directory. However this requires an * iget() for every directory which could be considered an excessive * amount of overhead. Since the inode for a mount point is always * in-core this is another argument for a call to get an inode if it * is in-core or NULL if it is not. */ static int cap_readdir(struct file * filp, void * dirent, filldir_t filldir) { ino_t type; int skip_dirs; struct hfs_brec brec; struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_CAP_RDIR); if (filp->f_pos == 0) { /* Entry 0 is for "." */ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, DT_DIR)) { return 0; } filp->f_pos = 1; } if (filp->f_pos == 1) { /* Entry 1 is for ".." */ hfs_u32 cnid; if (type == HFS_CAP_NDIR) { cnid = hfs_get_nl(entry->key.ParID); } else { cnid = entry->cnid; } if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) { return 0; } filp->f_pos = 2; } if (filp->f_pos < (dir->i_size - 3)) { hfs_u32 cnid; hfs_u8 type; if (hfs_cat_open(entry, &brec) || hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) { return 0; } while (filp->f_pos < (dir->i_size - 3)) { if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) { return 0; } if (!skip_dirs || (type != HFS_CDR_DIR)) { ino_t ino; unsigned int len; unsigned char tmp_name[HFS_NAMEMAX]; ino = ntohl(cnid) | HFS_I(dir)->file_type; len = hfs_namein(dir, tmp_name, &((struct hfs_cat_key *)brec.key)->CName); if (filldir(dirent, tmp_name, len, filp->f_pos, ino, DT_UNKNOWN)) { hfs_cat_close(entry, &brec); return 0; } } ++filp->f_pos; } hfs_cat_close(entry, &brec); } if (filp->f_pos == (dir->i_size - 3)) { if ((entry->cnid == htonl(HFS_ROOT_CNID)) && (type == HFS_CAP_NDIR)) { /* In root dir last-2 entry is for ".rootinfo" */ if (filldir(dirent, DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN, filp->f_pos, ntohl(entry->cnid) | HFS_CAP_FNDR, DT_UNKNOWN)) { return 0; } } ++filp->f_pos; } if (filp->f_pos == (dir->i_size - 2)) { if (type == HFS_CAP_NDIR) { /* In normal dirs last-1 entry is for ".finderinfo" */ if (filldir(dirent, DOT_FINDERINFO->Name, DOT_FINDERINFO_LEN, filp->f_pos, ntohl(entry->cnid) | HFS_CAP_FDIR, DT_UNKNOWN)) { return 0; } } ++filp->f_pos; } if (filp->f_pos == (dir->i_size - 1)) { if (type == HFS_CAP_NDIR) { /* In normal dirs last entry is for ".resource" */ if (filldir(dirent, DOT_RESOURCE->Name, DOT_RESOURCE_LEN, filp->f_pos, ntohl(entry->cnid) | HFS_CAP_RDIR, DT_UNKNOWN)) { return 0; } } ++filp->f_pos; } 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. */ 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; }
/* * nat_readdir() * * This is the readdir() entry in the file_operations structure for * HFS directories in the netatalk scheme. The purpose is to * enumerate the entries in a directory, given the inode of the * directory and a struct file which indicates the location in the * directory. The struct file is updated so that the next call with * the same dir and filp will produce the next directory entry. The * entries are returned in dirent, which is "filled-in" by calling * filldir(). This allows the same readdir() function be used for * different dirent formats. We try to read in as many entries as we * can before filldir() refuses to take any more. * * Note that the Netatalk format doesn't have the problem with * metadata for covered directories that exists in the other formats, * since the metadata is contained within the directory. */ static int nat_readdir(struct inode * dir, struct file * filp, void * dirent, filldir_t filldir) { ino_t type; int skip_dirs; struct hfs_brec brec; struct hfs_cat_entry *entry; if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { return -EBADF; } entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_NAT_HDIR); if (filp->f_pos == 0) { /* Entry 0 is for "." */ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) { return 0; } filp->f_pos = 1; } if (filp->f_pos == 1) { /* Entry 1 is for ".." */ hfs_u32 cnid; if (type == HFS_NAT_NDIR) { cnid = hfs_get_nl(entry->key.ParID); } else { cnid = entry->cnid; } if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1, ntohl(cnid))) { return 0; } filp->f_pos = 2; } if (filp->f_pos < (dir->i_size - 1)) { hfs_u32 cnid; hfs_u8 type; if (hfs_cat_open(entry, &brec) || hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) { return 0; } while (filp->f_pos < (dir->i_size - 1)) { if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) { return 0; } if (!skip_dirs || (type != HFS_CDR_DIR)) { ino_t ino; unsigned int len; unsigned char tmp_name[HFS_NAMEMAX]; ino = ntohl(cnid) | HFS_I(dir)->file_type; len = hfs_namein(dir, tmp_name, &((struct hfs_cat_key *)brec.key)->CName); if (filldir(dirent, tmp_name, len, filp->f_pos, ino)) { hfs_cat_close(entry, &brec); return 0; } } ++filp->f_pos; } hfs_cat_close(entry, &brec); } if (filp->f_pos == (dir->i_size - 1)) { if (type == HFS_NAT_NDIR) { /* In normal dirs entry 2 is for ".AppleDouble" */ if (filldir(dirent, DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN, filp->f_pos, ntohl(entry->cnid) | HFS_NAT_HDIR)) { return 0; } } else if (type == HFS_NAT_HDIR) { /* In .AppleDouble entry 2 is for ".Parent" */ if (filldir(dirent, DOT_PARENT->Name, DOT_PARENT_LEN, filp->f_pos, ntohl(entry->cnid) | HFS_NAT_HDR)) { return 0; } } ++filp->f_pos; } return 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; }