162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/fs/hfs/dir.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1995-1997 Paul H. Hargrove 562306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 662306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file contains directory-related functions independent of which 962306a36Sopenharmony_ci * scheme is being used to represent forks. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "hfs_fs.h" 1562306a36Sopenharmony_ci#include "btree.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * hfs_lookup() 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistatic struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry, 2162306a36Sopenharmony_ci unsigned int flags) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci hfs_cat_rec rec; 2462306a36Sopenharmony_ci struct hfs_find_data fd; 2562306a36Sopenharmony_ci struct inode *inode = NULL; 2662306a36Sopenharmony_ci int res; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); 2962306a36Sopenharmony_ci if (res) 3062306a36Sopenharmony_ci return ERR_PTR(res); 3162306a36Sopenharmony_ci hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name); 3262306a36Sopenharmony_ci res = hfs_brec_read(&fd, &rec, sizeof(rec)); 3362306a36Sopenharmony_ci if (res) { 3462306a36Sopenharmony_ci if (res != -ENOENT) 3562306a36Sopenharmony_ci inode = ERR_PTR(res); 3662306a36Sopenharmony_ci } else { 3762306a36Sopenharmony_ci inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec); 3862306a36Sopenharmony_ci if (!inode) 3962306a36Sopenharmony_ci inode = ERR_PTR(-EACCES); 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci hfs_find_exit(&fd); 4262306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * hfs_readdir 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic int hfs_readdir(struct file *file, struct dir_context *ctx) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 5162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 5262306a36Sopenharmony_ci int len, err; 5362306a36Sopenharmony_ci char strbuf[HFS_MAX_NAMELEN]; 5462306a36Sopenharmony_ci union hfs_cat_rec entry; 5562306a36Sopenharmony_ci struct hfs_find_data fd; 5662306a36Sopenharmony_ci struct hfs_readdir_data *rd; 5762306a36Sopenharmony_ci u16 type; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd); 6362306a36Sopenharmony_ci if (err) 6462306a36Sopenharmony_ci return err; 6562306a36Sopenharmony_ci hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); 6662306a36Sopenharmony_ci err = hfs_brec_find(&fd); 6762306a36Sopenharmony_ci if (err) 6862306a36Sopenharmony_ci goto out; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (ctx->pos == 0) { 7162306a36Sopenharmony_ci /* This is completely artificial... */ 7262306a36Sopenharmony_ci if (!dir_emit_dot(file, ctx)) 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci ctx->pos = 1; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (ctx->pos == 1) { 7762306a36Sopenharmony_ci if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { 7862306a36Sopenharmony_ci err = -EIO; 7962306a36Sopenharmony_ci goto out; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); 8362306a36Sopenharmony_ci if (entry.type != HFS_CDR_THD) { 8462306a36Sopenharmony_ci pr_err("bad catalog folder thread\n"); 8562306a36Sopenharmony_ci err = -EIO; 8662306a36Sopenharmony_ci goto out; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci //if (fd.entrylength < HFS_MIN_THREAD_SZ) { 8962306a36Sopenharmony_ci // pr_err("truncated catalog thread\n"); 9062306a36Sopenharmony_ci // err = -EIO; 9162306a36Sopenharmony_ci // goto out; 9262306a36Sopenharmony_ci //} 9362306a36Sopenharmony_ci if (!dir_emit(ctx, "..", 2, 9462306a36Sopenharmony_ci be32_to_cpu(entry.thread.ParID), DT_DIR)) 9562306a36Sopenharmony_ci goto out; 9662306a36Sopenharmony_ci ctx->pos = 2; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 9962306a36Sopenharmony_ci goto out; 10062306a36Sopenharmony_ci err = hfs_brec_goto(&fd, ctx->pos - 1); 10162306a36Sopenharmony_ci if (err) 10262306a36Sopenharmony_ci goto out; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (;;) { 10562306a36Sopenharmony_ci if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) { 10662306a36Sopenharmony_ci pr_err("walked past end of dir\n"); 10762306a36Sopenharmony_ci err = -EIO; 10862306a36Sopenharmony_ci goto out; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { 11262306a36Sopenharmony_ci err = -EIO; 11362306a36Sopenharmony_ci goto out; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); 11762306a36Sopenharmony_ci type = entry.type; 11862306a36Sopenharmony_ci len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName); 11962306a36Sopenharmony_ci if (type == HFS_CDR_DIR) { 12062306a36Sopenharmony_ci if (fd.entrylength < sizeof(struct hfs_cat_dir)) { 12162306a36Sopenharmony_ci pr_err("small dir entry\n"); 12262306a36Sopenharmony_ci err = -EIO; 12362306a36Sopenharmony_ci goto out; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci if (!dir_emit(ctx, strbuf, len, 12662306a36Sopenharmony_ci be32_to_cpu(entry.dir.DirID), DT_DIR)) 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci } else if (type == HFS_CDR_FIL) { 12962306a36Sopenharmony_ci if (fd.entrylength < sizeof(struct hfs_cat_file)) { 13062306a36Sopenharmony_ci pr_err("small file entry\n"); 13162306a36Sopenharmony_ci err = -EIO; 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci if (!dir_emit(ctx, strbuf, len, 13562306a36Sopenharmony_ci be32_to_cpu(entry.file.FlNum), DT_REG)) 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci pr_err("bad catalog entry type %d\n", type); 13962306a36Sopenharmony_ci err = -EIO; 14062306a36Sopenharmony_ci goto out; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci ctx->pos++; 14362306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 14462306a36Sopenharmony_ci goto out; 14562306a36Sopenharmony_ci err = hfs_brec_goto(&fd, 1); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto out; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci rd = file->private_data; 15062306a36Sopenharmony_ci if (!rd) { 15162306a36Sopenharmony_ci rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL); 15262306a36Sopenharmony_ci if (!rd) { 15362306a36Sopenharmony_ci err = -ENOMEM; 15462306a36Sopenharmony_ci goto out; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci file->private_data = rd; 15762306a36Sopenharmony_ci rd->file = file; 15862306a36Sopenharmony_ci spin_lock(&HFS_I(inode)->open_dir_lock); 15962306a36Sopenharmony_ci list_add(&rd->list, &HFS_I(inode)->open_dir_list); 16062306a36Sopenharmony_ci spin_unlock(&HFS_I(inode)->open_dir_lock); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Can be done after the list insertion; exclusion with 16462306a36Sopenharmony_ci * hfs_delete_cat() is provided by directory lock. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key)); 16762306a36Sopenharmony_ciout: 16862306a36Sopenharmony_ci hfs_find_exit(&fd); 16962306a36Sopenharmony_ci return err; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int hfs_dir_release(struct inode *inode, struct file *file) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct hfs_readdir_data *rd = file->private_data; 17562306a36Sopenharmony_ci if (rd) { 17662306a36Sopenharmony_ci spin_lock(&HFS_I(inode)->open_dir_lock); 17762306a36Sopenharmony_ci list_del(&rd->list); 17862306a36Sopenharmony_ci spin_unlock(&HFS_I(inode)->open_dir_lock); 17962306a36Sopenharmony_ci kfree(rd); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * hfs_create() 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * This is the create() entry in the inode_operations structure for 18862306a36Sopenharmony_ci * regular HFS directories. The purpose is to create a new file in 18962306a36Sopenharmony_ci * a directory and return a corresponding inode, given the inode for 19062306a36Sopenharmony_ci * the directory and the name (and its length) of the new file. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int hfs_create(struct mnt_idmap *idmap, struct inode *dir, 19362306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct inode *inode; 19662306a36Sopenharmony_ci int res; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci inode = hfs_new_inode(dir, &dentry->d_name, mode); 19962306a36Sopenharmony_ci if (!inode) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); 20362306a36Sopenharmony_ci if (res) { 20462306a36Sopenharmony_ci clear_nlink(inode); 20562306a36Sopenharmony_ci hfs_delete_inode(inode); 20662306a36Sopenharmony_ci iput(inode); 20762306a36Sopenharmony_ci return res; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci d_instantiate(dentry, inode); 21062306a36Sopenharmony_ci mark_inode_dirty(inode); 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * hfs_mkdir() 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * This is the mkdir() entry in the inode_operations structure for 21862306a36Sopenharmony_ci * regular HFS directories. The purpose is to create a new directory 21962306a36Sopenharmony_ci * in a directory, given the inode for the parent directory and the 22062306a36Sopenharmony_ci * name (and its length) of the new directory. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic int hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, 22362306a36Sopenharmony_ci struct dentry *dentry, umode_t mode) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct inode *inode; 22662306a36Sopenharmony_ci int res; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); 22962306a36Sopenharmony_ci if (!inode) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); 23362306a36Sopenharmony_ci if (res) { 23462306a36Sopenharmony_ci clear_nlink(inode); 23562306a36Sopenharmony_ci hfs_delete_inode(inode); 23662306a36Sopenharmony_ci iput(inode); 23762306a36Sopenharmony_ci return res; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci d_instantiate(dentry, inode); 24062306a36Sopenharmony_ci mark_inode_dirty(inode); 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * hfs_remove() 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * This serves as both unlink() and rmdir() in the inode_operations 24862306a36Sopenharmony_ci * structure for regular HFS directories. The purpose is to delete 24962306a36Sopenharmony_ci * an existing child, given the inode for the parent directory and 25062306a36Sopenharmony_ci * the name (and its length) of the existing directory. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * HFS does not have hardlinks, so both rmdir and unlink set the 25362306a36Sopenharmony_ci * link count to 0. The only difference is the emptiness check. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistatic int hfs_remove(struct inode *dir, struct dentry *dentry) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 25862306a36Sopenharmony_ci int res; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode) && inode->i_size != 2) 26162306a36Sopenharmony_ci return -ENOTEMPTY; 26262306a36Sopenharmony_ci res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); 26362306a36Sopenharmony_ci if (res) 26462306a36Sopenharmony_ci return res; 26562306a36Sopenharmony_ci clear_nlink(inode); 26662306a36Sopenharmony_ci inode_set_ctime_current(inode); 26762306a36Sopenharmony_ci hfs_delete_inode(inode); 26862306a36Sopenharmony_ci mark_inode_dirty(inode); 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* 27362306a36Sopenharmony_ci * hfs_rename() 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * This is the rename() entry in the inode_operations structure for 27662306a36Sopenharmony_ci * regular HFS directories. The purpose is to rename an existing 27762306a36Sopenharmony_ci * file or directory, given the inode for the current directory and 27862306a36Sopenharmony_ci * the name (and its length) of the existing file/directory and the 27962306a36Sopenharmony_ci * inode for the new directory and the name (and its length) of the 28062306a36Sopenharmony_ci * new file/directory. 28162306a36Sopenharmony_ci * XXX: how do you handle must_be dir? 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistatic int hfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, 28462306a36Sopenharmony_ci struct dentry *old_dentry, struct inode *new_dir, 28562306a36Sopenharmony_ci struct dentry *new_dentry, unsigned int flags) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int res; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (flags & ~RENAME_NOREPLACE) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Unlink destination if it already exists */ 29362306a36Sopenharmony_ci if (d_really_is_positive(new_dentry)) { 29462306a36Sopenharmony_ci res = hfs_remove(new_dir, new_dentry); 29562306a36Sopenharmony_ci if (res) 29662306a36Sopenharmony_ci return res; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci res = hfs_cat_move(d_inode(old_dentry)->i_ino, 30062306a36Sopenharmony_ci old_dir, &old_dentry->d_name, 30162306a36Sopenharmony_ci new_dir, &new_dentry->d_name); 30262306a36Sopenharmony_ci if (!res) 30362306a36Sopenharmony_ci hfs_cat_build_key(old_dir->i_sb, 30462306a36Sopenharmony_ci (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key, 30562306a36Sopenharmony_ci new_dir->i_ino, &new_dentry->d_name); 30662306a36Sopenharmony_ci return res; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciconst struct file_operations hfs_dir_operations = { 31062306a36Sopenharmony_ci .read = generic_read_dir, 31162306a36Sopenharmony_ci .iterate_shared = hfs_readdir, 31262306a36Sopenharmony_ci .llseek = generic_file_llseek, 31362306a36Sopenharmony_ci .release = hfs_dir_release, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciconst struct inode_operations hfs_dir_inode_operations = { 31762306a36Sopenharmony_ci .create = hfs_create, 31862306a36Sopenharmony_ci .lookup = hfs_lookup, 31962306a36Sopenharmony_ci .unlink = hfs_remove, 32062306a36Sopenharmony_ci .mkdir = hfs_mkdir, 32162306a36Sopenharmony_ci .rmdir = hfs_remove, 32262306a36Sopenharmony_ci .rename = hfs_rename, 32362306a36Sopenharmony_ci .setattr = hfs_inode_setattr, 32462306a36Sopenharmony_ci}; 325