1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * linux/fs/hfsplus/dir.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2001
6*4882a593Smuzhiyun * Brad Boyer (flar@allandria.com)
7*4882a593Smuzhiyun * (C) 2003 Ardis Technologies <roman@ardistech.com>
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Handling of directories
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/errno.h>
13*4882a593Smuzhiyun #include <linux/fs.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/random.h>
16*4882a593Smuzhiyun #include <linux/nls.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include "hfsplus_fs.h"
19*4882a593Smuzhiyun #include "hfsplus_raw.h"
20*4882a593Smuzhiyun #include "xattr.h"
21*4882a593Smuzhiyun
hfsplus_instantiate(struct dentry * dentry,struct inode * inode,u32 cnid)22*4882a593Smuzhiyun static inline void hfsplus_instantiate(struct dentry *dentry,
23*4882a593Smuzhiyun struct inode *inode, u32 cnid)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun dentry->d_fsdata = (void *)(unsigned long)cnid;
26*4882a593Smuzhiyun d_instantiate(dentry, inode);
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /* Find the entry inside dir named dentry->d_name */
hfsplus_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)30*4882a593Smuzhiyun static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
31*4882a593Smuzhiyun unsigned int flags)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun struct inode *inode = NULL;
34*4882a593Smuzhiyun struct hfs_find_data fd;
35*4882a593Smuzhiyun struct super_block *sb;
36*4882a593Smuzhiyun hfsplus_cat_entry entry;
37*4882a593Smuzhiyun int err;
38*4882a593Smuzhiyun u32 cnid, linkid = 0;
39*4882a593Smuzhiyun u16 type;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun sb = dir->i_sb;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun dentry->d_fsdata = NULL;
44*4882a593Smuzhiyun err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
45*4882a593Smuzhiyun if (err)
46*4882a593Smuzhiyun return ERR_PTR(err);
47*4882a593Smuzhiyun err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
48*4882a593Smuzhiyun &dentry->d_name);
49*4882a593Smuzhiyun if (unlikely(err < 0))
50*4882a593Smuzhiyun goto fail;
51*4882a593Smuzhiyun again:
52*4882a593Smuzhiyun err = hfs_brec_read(&fd, &entry, sizeof(entry));
53*4882a593Smuzhiyun if (err) {
54*4882a593Smuzhiyun if (err == -ENOENT) {
55*4882a593Smuzhiyun hfs_find_exit(&fd);
56*4882a593Smuzhiyun /* No such entry */
57*4882a593Smuzhiyun inode = NULL;
58*4882a593Smuzhiyun goto out;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun goto fail;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun type = be16_to_cpu(entry.type);
63*4882a593Smuzhiyun if (type == HFSPLUS_FOLDER) {
64*4882a593Smuzhiyun if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
65*4882a593Smuzhiyun err = -EIO;
66*4882a593Smuzhiyun goto fail;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun cnid = be32_to_cpu(entry.folder.id);
69*4882a593Smuzhiyun dentry->d_fsdata = (void *)(unsigned long)cnid;
70*4882a593Smuzhiyun } else if (type == HFSPLUS_FILE) {
71*4882a593Smuzhiyun if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
72*4882a593Smuzhiyun err = -EIO;
73*4882a593Smuzhiyun goto fail;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun cnid = be32_to_cpu(entry.file.id);
76*4882a593Smuzhiyun if (entry.file.user_info.fdType ==
77*4882a593Smuzhiyun cpu_to_be32(HFSP_HARDLINK_TYPE) &&
78*4882a593Smuzhiyun entry.file.user_info.fdCreator ==
79*4882a593Smuzhiyun cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
80*4882a593Smuzhiyun HFSPLUS_SB(sb)->hidden_dir &&
81*4882a593Smuzhiyun (entry.file.create_date ==
82*4882a593Smuzhiyun HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
83*4882a593Smuzhiyun create_date ||
84*4882a593Smuzhiyun entry.file.create_date ==
85*4882a593Smuzhiyun HFSPLUS_I(d_inode(sb->s_root))->
86*4882a593Smuzhiyun create_date)) {
87*4882a593Smuzhiyun struct qstr str;
88*4882a593Smuzhiyun char name[32];
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (dentry->d_fsdata) {
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun * We found a link pointing to another link,
93*4882a593Smuzhiyun * so ignore it and treat it as regular file.
94*4882a593Smuzhiyun */
95*4882a593Smuzhiyun cnid = (unsigned long)dentry->d_fsdata;
96*4882a593Smuzhiyun linkid = 0;
97*4882a593Smuzhiyun } else {
98*4882a593Smuzhiyun dentry->d_fsdata = (void *)(unsigned long)cnid;
99*4882a593Smuzhiyun linkid =
100*4882a593Smuzhiyun be32_to_cpu(entry.file.permissions.dev);
101*4882a593Smuzhiyun str.len = sprintf(name, "iNode%d", linkid);
102*4882a593Smuzhiyun str.name = name;
103*4882a593Smuzhiyun err = hfsplus_cat_build_key(sb, fd.search_key,
104*4882a593Smuzhiyun HFSPLUS_SB(sb)->hidden_dir->i_ino,
105*4882a593Smuzhiyun &str);
106*4882a593Smuzhiyun if (unlikely(err < 0))
107*4882a593Smuzhiyun goto fail;
108*4882a593Smuzhiyun goto again;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun } else if (!dentry->d_fsdata)
111*4882a593Smuzhiyun dentry->d_fsdata = (void *)(unsigned long)cnid;
112*4882a593Smuzhiyun } else {
113*4882a593Smuzhiyun pr_err("invalid catalog entry type in lookup\n");
114*4882a593Smuzhiyun err = -EIO;
115*4882a593Smuzhiyun goto fail;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun hfs_find_exit(&fd);
118*4882a593Smuzhiyun inode = hfsplus_iget(dir->i_sb, cnid);
119*4882a593Smuzhiyun if (IS_ERR(inode))
120*4882a593Smuzhiyun return ERR_CAST(inode);
121*4882a593Smuzhiyun if (S_ISREG(inode->i_mode))
122*4882a593Smuzhiyun HFSPLUS_I(inode)->linkid = linkid;
123*4882a593Smuzhiyun out:
124*4882a593Smuzhiyun return d_splice_alias(inode, dentry);
125*4882a593Smuzhiyun fail:
126*4882a593Smuzhiyun hfs_find_exit(&fd);
127*4882a593Smuzhiyun return ERR_PTR(err);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
hfsplus_readdir(struct file * file,struct dir_context * ctx)130*4882a593Smuzhiyun static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun struct inode *inode = file_inode(file);
133*4882a593Smuzhiyun struct super_block *sb = inode->i_sb;
134*4882a593Smuzhiyun int len, err;
135*4882a593Smuzhiyun char *strbuf;
136*4882a593Smuzhiyun hfsplus_cat_entry entry;
137*4882a593Smuzhiyun struct hfs_find_data fd;
138*4882a593Smuzhiyun struct hfsplus_readdir_data *rd;
139*4882a593Smuzhiyun u16 type;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun if (file->f_pos >= inode->i_size)
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
145*4882a593Smuzhiyun if (err)
146*4882a593Smuzhiyun return err;
147*4882a593Smuzhiyun strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
148*4882a593Smuzhiyun if (!strbuf) {
149*4882a593Smuzhiyun err = -ENOMEM;
150*4882a593Smuzhiyun goto out;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
153*4882a593Smuzhiyun err = hfs_brec_find(&fd, hfs_find_rec_by_key);
154*4882a593Smuzhiyun if (err)
155*4882a593Smuzhiyun goto out;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun if (ctx->pos == 0) {
158*4882a593Smuzhiyun /* This is completely artificial... */
159*4882a593Smuzhiyun if (!dir_emit_dot(file, ctx))
160*4882a593Smuzhiyun goto out;
161*4882a593Smuzhiyun ctx->pos = 1;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun if (ctx->pos == 1) {
164*4882a593Smuzhiyun if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
165*4882a593Smuzhiyun err = -EIO;
166*4882a593Smuzhiyun goto out;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
170*4882a593Smuzhiyun fd.entrylength);
171*4882a593Smuzhiyun if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
172*4882a593Smuzhiyun pr_err("bad catalog folder thread\n");
173*4882a593Smuzhiyun err = -EIO;
174*4882a593Smuzhiyun goto out;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
177*4882a593Smuzhiyun pr_err("truncated catalog thread\n");
178*4882a593Smuzhiyun err = -EIO;
179*4882a593Smuzhiyun goto out;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun if (!dir_emit(ctx, "..", 2,
182*4882a593Smuzhiyun be32_to_cpu(entry.thread.parentID), DT_DIR))
183*4882a593Smuzhiyun goto out;
184*4882a593Smuzhiyun ctx->pos = 2;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun if (ctx->pos >= inode->i_size)
187*4882a593Smuzhiyun goto out;
188*4882a593Smuzhiyun err = hfs_brec_goto(&fd, ctx->pos - 1);
189*4882a593Smuzhiyun if (err)
190*4882a593Smuzhiyun goto out;
191*4882a593Smuzhiyun for (;;) {
192*4882a593Smuzhiyun if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
193*4882a593Smuzhiyun pr_err("walked past end of dir\n");
194*4882a593Smuzhiyun err = -EIO;
195*4882a593Smuzhiyun goto out;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
199*4882a593Smuzhiyun err = -EIO;
200*4882a593Smuzhiyun goto out;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
204*4882a593Smuzhiyun fd.entrylength);
205*4882a593Smuzhiyun type = be16_to_cpu(entry.type);
206*4882a593Smuzhiyun len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
207*4882a593Smuzhiyun err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
208*4882a593Smuzhiyun if (err)
209*4882a593Smuzhiyun goto out;
210*4882a593Smuzhiyun if (type == HFSPLUS_FOLDER) {
211*4882a593Smuzhiyun if (fd.entrylength <
212*4882a593Smuzhiyun sizeof(struct hfsplus_cat_folder)) {
213*4882a593Smuzhiyun pr_err("small dir entry\n");
214*4882a593Smuzhiyun err = -EIO;
215*4882a593Smuzhiyun goto out;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun if (HFSPLUS_SB(sb)->hidden_dir &&
218*4882a593Smuzhiyun HFSPLUS_SB(sb)->hidden_dir->i_ino ==
219*4882a593Smuzhiyun be32_to_cpu(entry.folder.id))
220*4882a593Smuzhiyun goto next;
221*4882a593Smuzhiyun if (!dir_emit(ctx, strbuf, len,
222*4882a593Smuzhiyun be32_to_cpu(entry.folder.id), DT_DIR))
223*4882a593Smuzhiyun break;
224*4882a593Smuzhiyun } else if (type == HFSPLUS_FILE) {
225*4882a593Smuzhiyun u16 mode;
226*4882a593Smuzhiyun unsigned type = DT_UNKNOWN;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
229*4882a593Smuzhiyun pr_err("small file entry\n");
230*4882a593Smuzhiyun err = -EIO;
231*4882a593Smuzhiyun goto out;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun mode = be16_to_cpu(entry.file.permissions.mode);
235*4882a593Smuzhiyun if (S_ISREG(mode))
236*4882a593Smuzhiyun type = DT_REG;
237*4882a593Smuzhiyun else if (S_ISLNK(mode))
238*4882a593Smuzhiyun type = DT_LNK;
239*4882a593Smuzhiyun else if (S_ISFIFO(mode))
240*4882a593Smuzhiyun type = DT_FIFO;
241*4882a593Smuzhiyun else if (S_ISCHR(mode))
242*4882a593Smuzhiyun type = DT_CHR;
243*4882a593Smuzhiyun else if (S_ISBLK(mode))
244*4882a593Smuzhiyun type = DT_BLK;
245*4882a593Smuzhiyun else if (S_ISSOCK(mode))
246*4882a593Smuzhiyun type = DT_SOCK;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun if (!dir_emit(ctx, strbuf, len,
249*4882a593Smuzhiyun be32_to_cpu(entry.file.id), type))
250*4882a593Smuzhiyun break;
251*4882a593Smuzhiyun } else {
252*4882a593Smuzhiyun pr_err("bad catalog entry type\n");
253*4882a593Smuzhiyun err = -EIO;
254*4882a593Smuzhiyun goto out;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun next:
257*4882a593Smuzhiyun ctx->pos++;
258*4882a593Smuzhiyun if (ctx->pos >= inode->i_size)
259*4882a593Smuzhiyun goto out;
260*4882a593Smuzhiyun err = hfs_brec_goto(&fd, 1);
261*4882a593Smuzhiyun if (err)
262*4882a593Smuzhiyun goto out;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun rd = file->private_data;
265*4882a593Smuzhiyun if (!rd) {
266*4882a593Smuzhiyun rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
267*4882a593Smuzhiyun if (!rd) {
268*4882a593Smuzhiyun err = -ENOMEM;
269*4882a593Smuzhiyun goto out;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun file->private_data = rd;
272*4882a593Smuzhiyun rd->file = file;
273*4882a593Smuzhiyun spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
274*4882a593Smuzhiyun list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
275*4882a593Smuzhiyun spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun /*
278*4882a593Smuzhiyun * Can be done after the list insertion; exclusion with
279*4882a593Smuzhiyun * hfsplus_delete_cat() is provided by directory lock.
280*4882a593Smuzhiyun */
281*4882a593Smuzhiyun memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
282*4882a593Smuzhiyun out:
283*4882a593Smuzhiyun kfree(strbuf);
284*4882a593Smuzhiyun hfs_find_exit(&fd);
285*4882a593Smuzhiyun return err;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
hfsplus_dir_release(struct inode * inode,struct file * file)288*4882a593Smuzhiyun static int hfsplus_dir_release(struct inode *inode, struct file *file)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun struct hfsplus_readdir_data *rd = file->private_data;
291*4882a593Smuzhiyun if (rd) {
292*4882a593Smuzhiyun spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
293*4882a593Smuzhiyun list_del(&rd->list);
294*4882a593Smuzhiyun spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
295*4882a593Smuzhiyun kfree(rd);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun return 0;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
hfsplus_link(struct dentry * src_dentry,struct inode * dst_dir,struct dentry * dst_dentry)300*4882a593Smuzhiyun static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
301*4882a593Smuzhiyun struct dentry *dst_dentry)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
304*4882a593Smuzhiyun struct inode *inode = d_inode(src_dentry);
305*4882a593Smuzhiyun struct inode *src_dir = d_inode(src_dentry->d_parent);
306*4882a593Smuzhiyun struct qstr str;
307*4882a593Smuzhiyun char name[32];
308*4882a593Smuzhiyun u32 cnid, id;
309*4882a593Smuzhiyun int res;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun if (HFSPLUS_IS_RSRC(inode))
312*4882a593Smuzhiyun return -EPERM;
313*4882a593Smuzhiyun if (!S_ISREG(inode->i_mode))
314*4882a593Smuzhiyun return -EPERM;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun mutex_lock(&sbi->vh_mutex);
317*4882a593Smuzhiyun if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
318*4882a593Smuzhiyun for (;;) {
319*4882a593Smuzhiyun get_random_bytes(&id, sizeof(cnid));
320*4882a593Smuzhiyun id &= 0x3fffffff;
321*4882a593Smuzhiyun str.name = name;
322*4882a593Smuzhiyun str.len = sprintf(name, "iNode%d", id);
323*4882a593Smuzhiyun res = hfsplus_rename_cat(inode->i_ino,
324*4882a593Smuzhiyun src_dir, &src_dentry->d_name,
325*4882a593Smuzhiyun sbi->hidden_dir, &str);
326*4882a593Smuzhiyun if (!res)
327*4882a593Smuzhiyun break;
328*4882a593Smuzhiyun if (res != -EEXIST)
329*4882a593Smuzhiyun goto out;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun HFSPLUS_I(inode)->linkid = id;
332*4882a593Smuzhiyun cnid = sbi->next_cnid++;
333*4882a593Smuzhiyun src_dentry->d_fsdata = (void *)(unsigned long)cnid;
334*4882a593Smuzhiyun res = hfsplus_create_cat(cnid, src_dir,
335*4882a593Smuzhiyun &src_dentry->d_name, inode);
336*4882a593Smuzhiyun if (res)
337*4882a593Smuzhiyun /* panic? */
338*4882a593Smuzhiyun goto out;
339*4882a593Smuzhiyun sbi->file_count++;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun cnid = sbi->next_cnid++;
342*4882a593Smuzhiyun res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
343*4882a593Smuzhiyun if (res)
344*4882a593Smuzhiyun goto out;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun inc_nlink(inode);
347*4882a593Smuzhiyun hfsplus_instantiate(dst_dentry, inode, cnid);
348*4882a593Smuzhiyun ihold(inode);
349*4882a593Smuzhiyun inode->i_ctime = current_time(inode);
350*4882a593Smuzhiyun mark_inode_dirty(inode);
351*4882a593Smuzhiyun sbi->file_count++;
352*4882a593Smuzhiyun hfsplus_mark_mdb_dirty(dst_dir->i_sb);
353*4882a593Smuzhiyun out:
354*4882a593Smuzhiyun mutex_unlock(&sbi->vh_mutex);
355*4882a593Smuzhiyun return res;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
hfsplus_unlink(struct inode * dir,struct dentry * dentry)358*4882a593Smuzhiyun static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
359*4882a593Smuzhiyun {
360*4882a593Smuzhiyun struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
361*4882a593Smuzhiyun struct inode *inode = d_inode(dentry);
362*4882a593Smuzhiyun struct qstr str;
363*4882a593Smuzhiyun char name[32];
364*4882a593Smuzhiyun u32 cnid;
365*4882a593Smuzhiyun int res;
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun if (HFSPLUS_IS_RSRC(inode))
368*4882a593Smuzhiyun return -EPERM;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun mutex_lock(&sbi->vh_mutex);
371*4882a593Smuzhiyun cnid = (u32)(unsigned long)dentry->d_fsdata;
372*4882a593Smuzhiyun if (inode->i_ino == cnid &&
373*4882a593Smuzhiyun atomic_read(&HFSPLUS_I(inode)->opencnt)) {
374*4882a593Smuzhiyun str.name = name;
375*4882a593Smuzhiyun str.len = sprintf(name, "temp%lu", inode->i_ino);
376*4882a593Smuzhiyun res = hfsplus_rename_cat(inode->i_ino,
377*4882a593Smuzhiyun dir, &dentry->d_name,
378*4882a593Smuzhiyun sbi->hidden_dir, &str);
379*4882a593Smuzhiyun if (!res) {
380*4882a593Smuzhiyun inode->i_flags |= S_DEAD;
381*4882a593Smuzhiyun drop_nlink(inode);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun goto out;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
386*4882a593Smuzhiyun if (res)
387*4882a593Smuzhiyun goto out;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun if (inode->i_nlink > 0)
390*4882a593Smuzhiyun drop_nlink(inode);
391*4882a593Smuzhiyun if (inode->i_ino == cnid)
392*4882a593Smuzhiyun clear_nlink(inode);
393*4882a593Smuzhiyun if (!inode->i_nlink) {
394*4882a593Smuzhiyun if (inode->i_ino != cnid) {
395*4882a593Smuzhiyun sbi->file_count--;
396*4882a593Smuzhiyun if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
397*4882a593Smuzhiyun res = hfsplus_delete_cat(inode->i_ino,
398*4882a593Smuzhiyun sbi->hidden_dir,
399*4882a593Smuzhiyun NULL);
400*4882a593Smuzhiyun if (!res)
401*4882a593Smuzhiyun hfsplus_delete_inode(inode);
402*4882a593Smuzhiyun } else
403*4882a593Smuzhiyun inode->i_flags |= S_DEAD;
404*4882a593Smuzhiyun } else
405*4882a593Smuzhiyun hfsplus_delete_inode(inode);
406*4882a593Smuzhiyun } else
407*4882a593Smuzhiyun sbi->file_count--;
408*4882a593Smuzhiyun inode->i_ctime = current_time(inode);
409*4882a593Smuzhiyun mark_inode_dirty(inode);
410*4882a593Smuzhiyun out:
411*4882a593Smuzhiyun mutex_unlock(&sbi->vh_mutex);
412*4882a593Smuzhiyun return res;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun
hfsplus_rmdir(struct inode * dir,struct dentry * dentry)415*4882a593Smuzhiyun static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
418*4882a593Smuzhiyun struct inode *inode = d_inode(dentry);
419*4882a593Smuzhiyun int res;
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun if (inode->i_size != 2)
422*4882a593Smuzhiyun return -ENOTEMPTY;
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun mutex_lock(&sbi->vh_mutex);
425*4882a593Smuzhiyun res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
426*4882a593Smuzhiyun if (res)
427*4882a593Smuzhiyun goto out;
428*4882a593Smuzhiyun clear_nlink(inode);
429*4882a593Smuzhiyun inode->i_ctime = current_time(inode);
430*4882a593Smuzhiyun hfsplus_delete_inode(inode);
431*4882a593Smuzhiyun mark_inode_dirty(inode);
432*4882a593Smuzhiyun out:
433*4882a593Smuzhiyun mutex_unlock(&sbi->vh_mutex);
434*4882a593Smuzhiyun return res;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
hfsplus_symlink(struct inode * dir,struct dentry * dentry,const char * symname)437*4882a593Smuzhiyun static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
438*4882a593Smuzhiyun const char *symname)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
441*4882a593Smuzhiyun struct inode *inode;
442*4882a593Smuzhiyun int res = -ENOMEM;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun mutex_lock(&sbi->vh_mutex);
445*4882a593Smuzhiyun inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
446*4882a593Smuzhiyun if (!inode)
447*4882a593Smuzhiyun goto out;
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun res = page_symlink(inode, symname, strlen(symname) + 1);
450*4882a593Smuzhiyun if (res)
451*4882a593Smuzhiyun goto out_err;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
454*4882a593Smuzhiyun if (res)
455*4882a593Smuzhiyun goto out_err;
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun res = hfsplus_init_security(inode, dir, &dentry->d_name);
458*4882a593Smuzhiyun if (res == -EOPNOTSUPP)
459*4882a593Smuzhiyun res = 0; /* Operation is not supported. */
460*4882a593Smuzhiyun else if (res) {
461*4882a593Smuzhiyun /* Try to delete anyway without error analysis. */
462*4882a593Smuzhiyun hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
463*4882a593Smuzhiyun goto out_err;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun hfsplus_instantiate(dentry, inode, inode->i_ino);
467*4882a593Smuzhiyun mark_inode_dirty(inode);
468*4882a593Smuzhiyun goto out;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun out_err:
471*4882a593Smuzhiyun clear_nlink(inode);
472*4882a593Smuzhiyun hfsplus_delete_inode(inode);
473*4882a593Smuzhiyun iput(inode);
474*4882a593Smuzhiyun out:
475*4882a593Smuzhiyun mutex_unlock(&sbi->vh_mutex);
476*4882a593Smuzhiyun return res;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
hfsplus_mknod(struct inode * dir,struct dentry * dentry,umode_t mode,dev_t rdev)479*4882a593Smuzhiyun static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
480*4882a593Smuzhiyun umode_t mode, dev_t rdev)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
483*4882a593Smuzhiyun struct inode *inode;
484*4882a593Smuzhiyun int res = -ENOMEM;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun mutex_lock(&sbi->vh_mutex);
487*4882a593Smuzhiyun inode = hfsplus_new_inode(dir->i_sb, dir, mode);
488*4882a593Smuzhiyun if (!inode)
489*4882a593Smuzhiyun goto out;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
492*4882a593Smuzhiyun init_special_inode(inode, mode, rdev);
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
495*4882a593Smuzhiyun if (res)
496*4882a593Smuzhiyun goto failed_mknod;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun res = hfsplus_init_security(inode, dir, &dentry->d_name);
499*4882a593Smuzhiyun if (res == -EOPNOTSUPP)
500*4882a593Smuzhiyun res = 0; /* Operation is not supported. */
501*4882a593Smuzhiyun else if (res) {
502*4882a593Smuzhiyun /* Try to delete anyway without error analysis. */
503*4882a593Smuzhiyun hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
504*4882a593Smuzhiyun goto failed_mknod;
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun hfsplus_instantiate(dentry, inode, inode->i_ino);
508*4882a593Smuzhiyun mark_inode_dirty(inode);
509*4882a593Smuzhiyun goto out;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun failed_mknod:
512*4882a593Smuzhiyun clear_nlink(inode);
513*4882a593Smuzhiyun hfsplus_delete_inode(inode);
514*4882a593Smuzhiyun iput(inode);
515*4882a593Smuzhiyun out:
516*4882a593Smuzhiyun mutex_unlock(&sbi->vh_mutex);
517*4882a593Smuzhiyun return res;
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun
hfsplus_create(struct inode * dir,struct dentry * dentry,umode_t mode,bool excl)520*4882a593Smuzhiyun static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
521*4882a593Smuzhiyun bool excl)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun return hfsplus_mknod(dir, dentry, mode, 0);
524*4882a593Smuzhiyun }
525*4882a593Smuzhiyun
hfsplus_mkdir(struct inode * dir,struct dentry * dentry,umode_t mode)526*4882a593Smuzhiyun static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun
hfsplus_rename(struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry,unsigned int flags)531*4882a593Smuzhiyun static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
532*4882a593Smuzhiyun struct inode *new_dir, struct dentry *new_dentry,
533*4882a593Smuzhiyun unsigned int flags)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun int res;
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun if (flags & ~RENAME_NOREPLACE)
538*4882a593Smuzhiyun return -EINVAL;
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun /* Unlink destination if it already exists */
541*4882a593Smuzhiyun if (d_really_is_positive(new_dentry)) {
542*4882a593Smuzhiyun if (d_is_dir(new_dentry))
543*4882a593Smuzhiyun res = hfsplus_rmdir(new_dir, new_dentry);
544*4882a593Smuzhiyun else
545*4882a593Smuzhiyun res = hfsplus_unlink(new_dir, new_dentry);
546*4882a593Smuzhiyun if (res)
547*4882a593Smuzhiyun return res;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
551*4882a593Smuzhiyun old_dir, &old_dentry->d_name,
552*4882a593Smuzhiyun new_dir, &new_dentry->d_name);
553*4882a593Smuzhiyun if (!res)
554*4882a593Smuzhiyun new_dentry->d_fsdata = old_dentry->d_fsdata;
555*4882a593Smuzhiyun return res;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun const struct inode_operations hfsplus_dir_inode_operations = {
559*4882a593Smuzhiyun .lookup = hfsplus_lookup,
560*4882a593Smuzhiyun .create = hfsplus_create,
561*4882a593Smuzhiyun .link = hfsplus_link,
562*4882a593Smuzhiyun .unlink = hfsplus_unlink,
563*4882a593Smuzhiyun .mkdir = hfsplus_mkdir,
564*4882a593Smuzhiyun .rmdir = hfsplus_rmdir,
565*4882a593Smuzhiyun .symlink = hfsplus_symlink,
566*4882a593Smuzhiyun .mknod = hfsplus_mknod,
567*4882a593Smuzhiyun .rename = hfsplus_rename,
568*4882a593Smuzhiyun .getattr = hfsplus_getattr,
569*4882a593Smuzhiyun .listxattr = hfsplus_listxattr,
570*4882a593Smuzhiyun };
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun const struct file_operations hfsplus_dir_operations = {
573*4882a593Smuzhiyun .fsync = hfsplus_file_fsync,
574*4882a593Smuzhiyun .read = generic_read_dir,
575*4882a593Smuzhiyun .iterate_shared = hfsplus_readdir,
576*4882a593Smuzhiyun .unlocked_ioctl = hfsplus_ioctl,
577*4882a593Smuzhiyun .llseek = generic_file_llseek,
578*4882a593Smuzhiyun .release = hfsplus_dir_release,
579*4882a593Smuzhiyun };
580