162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/dir.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Handling of directories 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/random.h> 1662306a36Sopenharmony_ci#include <linux/nls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "hfsplus_fs.h" 1962306a36Sopenharmony_ci#include "hfsplus_raw.h" 2062306a36Sopenharmony_ci#include "xattr.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic inline void hfsplus_instantiate(struct dentry *dentry, 2362306a36Sopenharmony_ci struct inode *inode, u32 cnid) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci dentry->d_fsdata = (void *)(unsigned long)cnid; 2662306a36Sopenharmony_ci d_instantiate(dentry, inode); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Find the entry inside dir named dentry->d_name */ 3062306a36Sopenharmony_cistatic struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, 3162306a36Sopenharmony_ci unsigned int flags) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct inode *inode = NULL; 3462306a36Sopenharmony_ci struct hfs_find_data fd; 3562306a36Sopenharmony_ci struct super_block *sb; 3662306a36Sopenharmony_ci hfsplus_cat_entry entry; 3762306a36Sopenharmony_ci int err; 3862306a36Sopenharmony_ci u32 cnid, linkid = 0; 3962306a36Sopenharmony_ci u16 type; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci sb = dir->i_sb; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci dentry->d_fsdata = NULL; 4462306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); 4562306a36Sopenharmony_ci if (err) 4662306a36Sopenharmony_ci return ERR_PTR(err); 4762306a36Sopenharmony_ci err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, 4862306a36Sopenharmony_ci &dentry->d_name); 4962306a36Sopenharmony_ci if (unlikely(err < 0)) 5062306a36Sopenharmony_ci goto fail; 5162306a36Sopenharmony_ciagain: 5262306a36Sopenharmony_ci err = hfs_brec_read(&fd, &entry, sizeof(entry)); 5362306a36Sopenharmony_ci if (err) { 5462306a36Sopenharmony_ci if (err == -ENOENT) { 5562306a36Sopenharmony_ci hfs_find_exit(&fd); 5662306a36Sopenharmony_ci /* No such entry */ 5762306a36Sopenharmony_ci inode = NULL; 5862306a36Sopenharmony_ci goto out; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci goto fail; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci type = be16_to_cpu(entry.type); 6362306a36Sopenharmony_ci if (type == HFSPLUS_FOLDER) { 6462306a36Sopenharmony_ci if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { 6562306a36Sopenharmony_ci err = -EIO; 6662306a36Sopenharmony_ci goto fail; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci cnid = be32_to_cpu(entry.folder.id); 6962306a36Sopenharmony_ci dentry->d_fsdata = (void *)(unsigned long)cnid; 7062306a36Sopenharmony_ci } else if (type == HFSPLUS_FILE) { 7162306a36Sopenharmony_ci if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { 7262306a36Sopenharmony_ci err = -EIO; 7362306a36Sopenharmony_ci goto fail; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci cnid = be32_to_cpu(entry.file.id); 7662306a36Sopenharmony_ci if (entry.file.user_info.fdType == 7762306a36Sopenharmony_ci cpu_to_be32(HFSP_HARDLINK_TYPE) && 7862306a36Sopenharmony_ci entry.file.user_info.fdCreator == 7962306a36Sopenharmony_ci cpu_to_be32(HFSP_HFSPLUS_CREATOR) && 8062306a36Sopenharmony_ci HFSPLUS_SB(sb)->hidden_dir && 8162306a36Sopenharmony_ci (entry.file.create_date == 8262306a36Sopenharmony_ci HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)-> 8362306a36Sopenharmony_ci create_date || 8462306a36Sopenharmony_ci entry.file.create_date == 8562306a36Sopenharmony_ci HFSPLUS_I(d_inode(sb->s_root))-> 8662306a36Sopenharmony_ci create_date)) { 8762306a36Sopenharmony_ci struct qstr str; 8862306a36Sopenharmony_ci char name[32]; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (dentry->d_fsdata) { 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * We found a link pointing to another link, 9362306a36Sopenharmony_ci * so ignore it and treat it as regular file. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci cnid = (unsigned long)dentry->d_fsdata; 9662306a36Sopenharmony_ci linkid = 0; 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci dentry->d_fsdata = (void *)(unsigned long)cnid; 9962306a36Sopenharmony_ci linkid = 10062306a36Sopenharmony_ci be32_to_cpu(entry.file.permissions.dev); 10162306a36Sopenharmony_ci str.len = sprintf(name, "iNode%d", linkid); 10262306a36Sopenharmony_ci str.name = name; 10362306a36Sopenharmony_ci err = hfsplus_cat_build_key(sb, fd.search_key, 10462306a36Sopenharmony_ci HFSPLUS_SB(sb)->hidden_dir->i_ino, 10562306a36Sopenharmony_ci &str); 10662306a36Sopenharmony_ci if (unlikely(err < 0)) 10762306a36Sopenharmony_ci goto fail; 10862306a36Sopenharmony_ci goto again; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } else if (!dentry->d_fsdata) 11162306a36Sopenharmony_ci dentry->d_fsdata = (void *)(unsigned long)cnid; 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci pr_err("invalid catalog entry type in lookup\n"); 11462306a36Sopenharmony_ci err = -EIO; 11562306a36Sopenharmony_ci goto fail; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci hfs_find_exit(&fd); 11862306a36Sopenharmony_ci inode = hfsplus_iget(dir->i_sb, cnid); 11962306a36Sopenharmony_ci if (IS_ERR(inode)) 12062306a36Sopenharmony_ci return ERR_CAST(inode); 12162306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) 12262306a36Sopenharmony_ci HFSPLUS_I(inode)->linkid = linkid; 12362306a36Sopenharmony_ciout: 12462306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 12562306a36Sopenharmony_cifail: 12662306a36Sopenharmony_ci hfs_find_exit(&fd); 12762306a36Sopenharmony_ci return ERR_PTR(err); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int hfsplus_readdir(struct file *file, struct dir_context *ctx) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 13362306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 13462306a36Sopenharmony_ci int len, err; 13562306a36Sopenharmony_ci char *strbuf; 13662306a36Sopenharmony_ci hfsplus_cat_entry entry; 13762306a36Sopenharmony_ci struct hfs_find_data fd; 13862306a36Sopenharmony_ci struct hfsplus_readdir_data *rd; 13962306a36Sopenharmony_ci u16 type; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (file->f_pos >= inode->i_size) 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); 14562306a36Sopenharmony_ci if (err) 14662306a36Sopenharmony_ci return err; 14762306a36Sopenharmony_ci strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL); 14862306a36Sopenharmony_ci if (!strbuf) { 14962306a36Sopenharmony_ci err = -ENOMEM; 15062306a36Sopenharmony_ci goto out; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino); 15362306a36Sopenharmony_ci err = hfs_brec_find(&fd, hfs_find_rec_by_key); 15462306a36Sopenharmony_ci if (err) 15562306a36Sopenharmony_ci goto out; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (ctx->pos == 0) { 15862306a36Sopenharmony_ci /* This is completely artificial... */ 15962306a36Sopenharmony_ci if (!dir_emit_dot(file, ctx)) 16062306a36Sopenharmony_ci goto out; 16162306a36Sopenharmony_ci ctx->pos = 1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci if (ctx->pos == 1) { 16462306a36Sopenharmony_ci if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { 16562306a36Sopenharmony_ci err = -EIO; 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 17062306a36Sopenharmony_ci fd.entrylength); 17162306a36Sopenharmony_ci if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { 17262306a36Sopenharmony_ci pr_err("bad catalog folder thread\n"); 17362306a36Sopenharmony_ci err = -EIO; 17462306a36Sopenharmony_ci goto out; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) { 17762306a36Sopenharmony_ci pr_err("truncated catalog thread\n"); 17862306a36Sopenharmony_ci err = -EIO; 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (!dir_emit(ctx, "..", 2, 18262306a36Sopenharmony_ci be32_to_cpu(entry.thread.parentID), DT_DIR)) 18362306a36Sopenharmony_ci goto out; 18462306a36Sopenharmony_ci ctx->pos = 2; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci err = hfs_brec_goto(&fd, ctx->pos - 1); 18962306a36Sopenharmony_ci if (err) 19062306a36Sopenharmony_ci goto out; 19162306a36Sopenharmony_ci for (;;) { 19262306a36Sopenharmony_ci if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) { 19362306a36Sopenharmony_ci pr_err("walked past end of dir\n"); 19462306a36Sopenharmony_ci err = -EIO; 19562306a36Sopenharmony_ci goto out; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) { 19962306a36Sopenharmony_ci err = -EIO; 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 20462306a36Sopenharmony_ci fd.entrylength); 20562306a36Sopenharmony_ci type = be16_to_cpu(entry.type); 20662306a36Sopenharmony_ci len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN; 20762306a36Sopenharmony_ci err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); 20862306a36Sopenharmony_ci if (err) 20962306a36Sopenharmony_ci goto out; 21062306a36Sopenharmony_ci if (type == HFSPLUS_FOLDER) { 21162306a36Sopenharmony_ci if (fd.entrylength < 21262306a36Sopenharmony_ci sizeof(struct hfsplus_cat_folder)) { 21362306a36Sopenharmony_ci pr_err("small dir entry\n"); 21462306a36Sopenharmony_ci err = -EIO; 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci if (HFSPLUS_SB(sb)->hidden_dir && 21862306a36Sopenharmony_ci HFSPLUS_SB(sb)->hidden_dir->i_ino == 21962306a36Sopenharmony_ci be32_to_cpu(entry.folder.id)) 22062306a36Sopenharmony_ci goto next; 22162306a36Sopenharmony_ci if (!dir_emit(ctx, strbuf, len, 22262306a36Sopenharmony_ci be32_to_cpu(entry.folder.id), DT_DIR)) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } else if (type == HFSPLUS_FILE) { 22562306a36Sopenharmony_ci u16 mode; 22662306a36Sopenharmony_ci unsigned type = DT_UNKNOWN; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { 22962306a36Sopenharmony_ci pr_err("small file entry\n"); 23062306a36Sopenharmony_ci err = -EIO; 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mode = be16_to_cpu(entry.file.permissions.mode); 23562306a36Sopenharmony_ci if (S_ISREG(mode)) 23662306a36Sopenharmony_ci type = DT_REG; 23762306a36Sopenharmony_ci else if (S_ISLNK(mode)) 23862306a36Sopenharmony_ci type = DT_LNK; 23962306a36Sopenharmony_ci else if (S_ISFIFO(mode)) 24062306a36Sopenharmony_ci type = DT_FIFO; 24162306a36Sopenharmony_ci else if (S_ISCHR(mode)) 24262306a36Sopenharmony_ci type = DT_CHR; 24362306a36Sopenharmony_ci else if (S_ISBLK(mode)) 24462306a36Sopenharmony_ci type = DT_BLK; 24562306a36Sopenharmony_ci else if (S_ISSOCK(mode)) 24662306a36Sopenharmony_ci type = DT_SOCK; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!dir_emit(ctx, strbuf, len, 24962306a36Sopenharmony_ci be32_to_cpu(entry.file.id), type)) 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } else { 25262306a36Sopenharmony_ci pr_err("bad catalog entry type\n"); 25362306a36Sopenharmony_ci err = -EIO; 25462306a36Sopenharmony_ci goto out; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_cinext: 25762306a36Sopenharmony_ci ctx->pos++; 25862306a36Sopenharmony_ci if (ctx->pos >= inode->i_size) 25962306a36Sopenharmony_ci goto out; 26062306a36Sopenharmony_ci err = hfs_brec_goto(&fd, 1); 26162306a36Sopenharmony_ci if (err) 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci rd = file->private_data; 26562306a36Sopenharmony_ci if (!rd) { 26662306a36Sopenharmony_ci rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL); 26762306a36Sopenharmony_ci if (!rd) { 26862306a36Sopenharmony_ci err = -ENOMEM; 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci file->private_data = rd; 27262306a36Sopenharmony_ci rd->file = file; 27362306a36Sopenharmony_ci spin_lock(&HFSPLUS_I(inode)->open_dir_lock); 27462306a36Sopenharmony_ci list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); 27562306a36Sopenharmony_ci spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * Can be done after the list insertion; exclusion with 27962306a36Sopenharmony_ci * hfsplus_delete_cat() is provided by directory lock. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); 28262306a36Sopenharmony_ciout: 28362306a36Sopenharmony_ci kfree(strbuf); 28462306a36Sopenharmony_ci hfs_find_exit(&fd); 28562306a36Sopenharmony_ci return err; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int hfsplus_dir_release(struct inode *inode, struct file *file) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct hfsplus_readdir_data *rd = file->private_data; 29162306a36Sopenharmony_ci if (rd) { 29262306a36Sopenharmony_ci spin_lock(&HFSPLUS_I(inode)->open_dir_lock); 29362306a36Sopenharmony_ci list_del(&rd->list); 29462306a36Sopenharmony_ci spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); 29562306a36Sopenharmony_ci kfree(rd); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, 30162306a36Sopenharmony_ci struct dentry *dst_dentry) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb); 30462306a36Sopenharmony_ci struct inode *inode = d_inode(src_dentry); 30562306a36Sopenharmony_ci struct inode *src_dir = d_inode(src_dentry->d_parent); 30662306a36Sopenharmony_ci struct qstr str; 30762306a36Sopenharmony_ci char name[32]; 30862306a36Sopenharmony_ci u32 cnid, id; 30962306a36Sopenharmony_ci int res; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (HFSPLUS_IS_RSRC(inode)) 31262306a36Sopenharmony_ci return -EPERM; 31362306a36Sopenharmony_ci if (!S_ISREG(inode->i_mode)) 31462306a36Sopenharmony_ci return -EPERM; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 31762306a36Sopenharmony_ci if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { 31862306a36Sopenharmony_ci for (;;) { 31962306a36Sopenharmony_ci get_random_bytes(&id, sizeof(cnid)); 32062306a36Sopenharmony_ci id &= 0x3fffffff; 32162306a36Sopenharmony_ci str.name = name; 32262306a36Sopenharmony_ci str.len = sprintf(name, "iNode%d", id); 32362306a36Sopenharmony_ci res = hfsplus_rename_cat(inode->i_ino, 32462306a36Sopenharmony_ci src_dir, &src_dentry->d_name, 32562306a36Sopenharmony_ci sbi->hidden_dir, &str); 32662306a36Sopenharmony_ci if (!res) 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci if (res != -EEXIST) 32962306a36Sopenharmony_ci goto out; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci HFSPLUS_I(inode)->linkid = id; 33262306a36Sopenharmony_ci cnid = sbi->next_cnid++; 33362306a36Sopenharmony_ci src_dentry->d_fsdata = (void *)(unsigned long)cnid; 33462306a36Sopenharmony_ci res = hfsplus_create_cat(cnid, src_dir, 33562306a36Sopenharmony_ci &src_dentry->d_name, inode); 33662306a36Sopenharmony_ci if (res) 33762306a36Sopenharmony_ci /* panic? */ 33862306a36Sopenharmony_ci goto out; 33962306a36Sopenharmony_ci sbi->file_count++; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci cnid = sbi->next_cnid++; 34262306a36Sopenharmony_ci res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); 34362306a36Sopenharmony_ci if (res) 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci inc_nlink(inode); 34762306a36Sopenharmony_ci hfsplus_instantiate(dst_dentry, inode, cnid); 34862306a36Sopenharmony_ci ihold(inode); 34962306a36Sopenharmony_ci inode_set_ctime_current(inode); 35062306a36Sopenharmony_ci mark_inode_dirty(inode); 35162306a36Sopenharmony_ci sbi->file_count++; 35262306a36Sopenharmony_ci hfsplus_mark_mdb_dirty(dst_dir->i_sb); 35362306a36Sopenharmony_ciout: 35462306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 35562306a36Sopenharmony_ci return res; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int hfsplus_unlink(struct inode *dir, struct dentry *dentry) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 36162306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 36262306a36Sopenharmony_ci struct qstr str; 36362306a36Sopenharmony_ci char name[32]; 36462306a36Sopenharmony_ci u32 cnid; 36562306a36Sopenharmony_ci int res; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (HFSPLUS_IS_RSRC(inode)) 36862306a36Sopenharmony_ci return -EPERM; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 37162306a36Sopenharmony_ci cnid = (u32)(unsigned long)dentry->d_fsdata; 37262306a36Sopenharmony_ci if (inode->i_ino == cnid && 37362306a36Sopenharmony_ci atomic_read(&HFSPLUS_I(inode)->opencnt)) { 37462306a36Sopenharmony_ci str.name = name; 37562306a36Sopenharmony_ci str.len = sprintf(name, "temp%lu", inode->i_ino); 37662306a36Sopenharmony_ci res = hfsplus_rename_cat(inode->i_ino, 37762306a36Sopenharmony_ci dir, &dentry->d_name, 37862306a36Sopenharmony_ci sbi->hidden_dir, &str); 37962306a36Sopenharmony_ci if (!res) { 38062306a36Sopenharmony_ci inode->i_flags |= S_DEAD; 38162306a36Sopenharmony_ci drop_nlink(inode); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci goto out; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); 38662306a36Sopenharmony_ci if (res) 38762306a36Sopenharmony_ci goto out; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (inode->i_nlink > 0) 39062306a36Sopenharmony_ci drop_nlink(inode); 39162306a36Sopenharmony_ci if (inode->i_ino == cnid) 39262306a36Sopenharmony_ci clear_nlink(inode); 39362306a36Sopenharmony_ci if (!inode->i_nlink) { 39462306a36Sopenharmony_ci if (inode->i_ino != cnid) { 39562306a36Sopenharmony_ci sbi->file_count--; 39662306a36Sopenharmony_ci if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) { 39762306a36Sopenharmony_ci res = hfsplus_delete_cat(inode->i_ino, 39862306a36Sopenharmony_ci sbi->hidden_dir, 39962306a36Sopenharmony_ci NULL); 40062306a36Sopenharmony_ci if (!res) 40162306a36Sopenharmony_ci hfsplus_delete_inode(inode); 40262306a36Sopenharmony_ci } else 40362306a36Sopenharmony_ci inode->i_flags |= S_DEAD; 40462306a36Sopenharmony_ci } else 40562306a36Sopenharmony_ci hfsplus_delete_inode(inode); 40662306a36Sopenharmony_ci } else 40762306a36Sopenharmony_ci sbi->file_count--; 40862306a36Sopenharmony_ci inode_set_ctime_current(inode); 40962306a36Sopenharmony_ci mark_inode_dirty(inode); 41062306a36Sopenharmony_ciout: 41162306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 41262306a36Sopenharmony_ci return res; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 41862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 41962306a36Sopenharmony_ci int res; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (inode->i_size != 2) 42262306a36Sopenharmony_ci return -ENOTEMPTY; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 42562306a36Sopenharmony_ci res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); 42662306a36Sopenharmony_ci if (res) 42762306a36Sopenharmony_ci goto out; 42862306a36Sopenharmony_ci clear_nlink(inode); 42962306a36Sopenharmony_ci inode_set_ctime_current(inode); 43062306a36Sopenharmony_ci hfsplus_delete_inode(inode); 43162306a36Sopenharmony_ci mark_inode_dirty(inode); 43262306a36Sopenharmony_ciout: 43362306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 43462306a36Sopenharmony_ci return res; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int hfsplus_symlink(struct mnt_idmap *idmap, struct inode *dir, 43862306a36Sopenharmony_ci struct dentry *dentry, const char *symname) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 44162306a36Sopenharmony_ci struct inode *inode; 44262306a36Sopenharmony_ci int res = -ENOMEM; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 44562306a36Sopenharmony_ci inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO); 44662306a36Sopenharmony_ci if (!inode) 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci res = page_symlink(inode, symname, strlen(symname) + 1); 45062306a36Sopenharmony_ci if (res) 45162306a36Sopenharmony_ci goto out_err; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); 45462306a36Sopenharmony_ci if (res) 45562306a36Sopenharmony_ci goto out_err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci res = hfsplus_init_security(inode, dir, &dentry->d_name); 45862306a36Sopenharmony_ci if (res == -EOPNOTSUPP) 45962306a36Sopenharmony_ci res = 0; /* Operation is not supported. */ 46062306a36Sopenharmony_ci else if (res) { 46162306a36Sopenharmony_ci /* Try to delete anyway without error analysis. */ 46262306a36Sopenharmony_ci hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); 46362306a36Sopenharmony_ci goto out_err; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci hfsplus_instantiate(dentry, inode, inode->i_ino); 46762306a36Sopenharmony_ci mark_inode_dirty(inode); 46862306a36Sopenharmony_ci goto out; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciout_err: 47162306a36Sopenharmony_ci clear_nlink(inode); 47262306a36Sopenharmony_ci hfsplus_delete_inode(inode); 47362306a36Sopenharmony_ci iput(inode); 47462306a36Sopenharmony_ciout: 47562306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 47662306a36Sopenharmony_ci return res; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int hfsplus_mknod(struct mnt_idmap *idmap, struct inode *dir, 48062306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, dev_t rdev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 48362306a36Sopenharmony_ci struct inode *inode; 48462306a36Sopenharmony_ci int res = -ENOMEM; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 48762306a36Sopenharmony_ci inode = hfsplus_new_inode(dir->i_sb, dir, mode); 48862306a36Sopenharmony_ci if (!inode) 48962306a36Sopenharmony_ci goto out; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) 49262306a36Sopenharmony_ci init_special_inode(inode, mode, rdev); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); 49562306a36Sopenharmony_ci if (res) 49662306a36Sopenharmony_ci goto failed_mknod; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci res = hfsplus_init_security(inode, dir, &dentry->d_name); 49962306a36Sopenharmony_ci if (res == -EOPNOTSUPP) 50062306a36Sopenharmony_ci res = 0; /* Operation is not supported. */ 50162306a36Sopenharmony_ci else if (res) { 50262306a36Sopenharmony_ci /* Try to delete anyway without error analysis. */ 50362306a36Sopenharmony_ci hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); 50462306a36Sopenharmony_ci goto failed_mknod; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci hfsplus_instantiate(dentry, inode, inode->i_ino); 50862306a36Sopenharmony_ci mark_inode_dirty(inode); 50962306a36Sopenharmony_ci goto out; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cifailed_mknod: 51262306a36Sopenharmony_ci clear_nlink(inode); 51362306a36Sopenharmony_ci hfsplus_delete_inode(inode); 51462306a36Sopenharmony_ci iput(inode); 51562306a36Sopenharmony_ciout: 51662306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 51762306a36Sopenharmony_ci return res; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int hfsplus_create(struct mnt_idmap *idmap, struct inode *dir, 52162306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci return hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode, 0); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int hfsplus_mkdir(struct mnt_idmap *idmap, struct inode *dir, 52762306a36Sopenharmony_ci struct dentry *dentry, umode_t mode) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci return hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int hfsplus_rename(struct mnt_idmap *idmap, 53362306a36Sopenharmony_ci struct inode *old_dir, struct dentry *old_dentry, 53462306a36Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 53562306a36Sopenharmony_ci unsigned int flags) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci int res; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (flags & ~RENAME_NOREPLACE) 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Unlink destination if it already exists */ 54362306a36Sopenharmony_ci if (d_really_is_positive(new_dentry)) { 54462306a36Sopenharmony_ci if (d_is_dir(new_dentry)) 54562306a36Sopenharmony_ci res = hfsplus_rmdir(new_dir, new_dentry); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci res = hfsplus_unlink(new_dir, new_dentry); 54862306a36Sopenharmony_ci if (res) 54962306a36Sopenharmony_ci return res; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, 55362306a36Sopenharmony_ci old_dir, &old_dentry->d_name, 55462306a36Sopenharmony_ci new_dir, &new_dentry->d_name); 55562306a36Sopenharmony_ci if (!res) 55662306a36Sopenharmony_ci new_dentry->d_fsdata = old_dentry->d_fsdata; 55762306a36Sopenharmony_ci return res; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciconst struct inode_operations hfsplus_dir_inode_operations = { 56162306a36Sopenharmony_ci .lookup = hfsplus_lookup, 56262306a36Sopenharmony_ci .create = hfsplus_create, 56362306a36Sopenharmony_ci .link = hfsplus_link, 56462306a36Sopenharmony_ci .unlink = hfsplus_unlink, 56562306a36Sopenharmony_ci .mkdir = hfsplus_mkdir, 56662306a36Sopenharmony_ci .rmdir = hfsplus_rmdir, 56762306a36Sopenharmony_ci .symlink = hfsplus_symlink, 56862306a36Sopenharmony_ci .mknod = hfsplus_mknod, 56962306a36Sopenharmony_ci .rename = hfsplus_rename, 57062306a36Sopenharmony_ci .getattr = hfsplus_getattr, 57162306a36Sopenharmony_ci .listxattr = hfsplus_listxattr, 57262306a36Sopenharmony_ci .fileattr_get = hfsplus_fileattr_get, 57362306a36Sopenharmony_ci .fileattr_set = hfsplus_fileattr_set, 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciconst struct file_operations hfsplus_dir_operations = { 57762306a36Sopenharmony_ci .fsync = hfsplus_file_fsync, 57862306a36Sopenharmony_ci .read = generic_read_dir, 57962306a36Sopenharmony_ci .iterate_shared = hfsplus_readdir, 58062306a36Sopenharmony_ci .unlocked_ioctl = hfsplus_ioctl, 58162306a36Sopenharmony_ci .llseek = generic_file_llseek, 58262306a36Sopenharmony_ci .release = hfsplus_dir_release, 58362306a36Sopenharmony_ci}; 584