/* * 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; }
/* * 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; }
/* * 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; }
/* * nat_rmdir() * * This is the rmdir() entry in the inode_operations structure for * Netatalk directories. The purpose is to delete an existing * directory, given the inode for the parent directory and the name * (and its length) of the existing directory. * * We handle .AppleDouble and call hfs_rmdir() for all other cases. */ static int nat_rmdir(struct inode *parent, const char *name, int len) { struct hfs_cat_entry *entry = HFS_I(parent)->entry; struct hfs_name cname; int error; hfs_nameout(parent, &cname, name, len); if (hfs_streq(&cname, DOT_APPLEDOUBLE)) { if (!HFS_SB(parent->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; } else if (entry->u.dir.files || entry->u.dir.dirs) { /* AFPD compatible, but the directory is not empty */ error = -ENOTEMPTY; } else { /* AFPD compatible, so pretend to succeed */ error = 0; } iput(parent); } else { error = hfs_rmdir(parent, name, len); } return error; }
/* * 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; }