xref: /openbmc/linux/fs/hfsplus/dir.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  *  linux/fs/hfsplus/dir.c
3*1da177e4SLinus Torvalds  *
4*1da177e4SLinus Torvalds  * Copyright (C) 2001
5*1da177e4SLinus Torvalds  * Brad Boyer (flar@allandria.com)
6*1da177e4SLinus Torvalds  * (C) 2003 Ardis Technologies <roman@ardistech.com>
7*1da177e4SLinus Torvalds  *
8*1da177e4SLinus Torvalds  * Handling of directories
9*1da177e4SLinus Torvalds  */
10*1da177e4SLinus Torvalds 
11*1da177e4SLinus Torvalds #include <linux/errno.h>
12*1da177e4SLinus Torvalds #include <linux/fs.h>
13*1da177e4SLinus Torvalds #include <linux/sched.h>
14*1da177e4SLinus Torvalds #include <linux/slab.h>
15*1da177e4SLinus Torvalds #include <linux/random.h>
16*1da177e4SLinus Torvalds #include <linux/version.h>
17*1da177e4SLinus Torvalds 
18*1da177e4SLinus Torvalds #include "hfsplus_fs.h"
19*1da177e4SLinus Torvalds #include "hfsplus_raw.h"
20*1da177e4SLinus Torvalds 
21*1da177e4SLinus Torvalds static inline void hfsplus_instantiate(struct dentry *dentry,
22*1da177e4SLinus Torvalds 				       struct inode *inode, u32 cnid)
23*1da177e4SLinus Torvalds {
24*1da177e4SLinus Torvalds 	dentry->d_fsdata = (void *)(unsigned long)cnid;
25*1da177e4SLinus Torvalds 	d_instantiate(dentry, inode);
26*1da177e4SLinus Torvalds }
27*1da177e4SLinus Torvalds 
28*1da177e4SLinus Torvalds /* Find the entry inside dir named dentry->d_name */
29*1da177e4SLinus Torvalds static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
30*1da177e4SLinus Torvalds 				     struct nameidata *nd)
31*1da177e4SLinus Torvalds {
32*1da177e4SLinus Torvalds 	struct inode *inode = NULL;
33*1da177e4SLinus Torvalds 	struct hfs_find_data fd;
34*1da177e4SLinus Torvalds 	struct super_block *sb;
35*1da177e4SLinus Torvalds 	hfsplus_cat_entry entry;
36*1da177e4SLinus Torvalds 	int err;
37*1da177e4SLinus Torvalds 	u32 cnid, linkid = 0;
38*1da177e4SLinus Torvalds 	u16 type;
39*1da177e4SLinus Torvalds 
40*1da177e4SLinus Torvalds 	sb = dir->i_sb;
41*1da177e4SLinus Torvalds 	dentry->d_fsdata = NULL;
42*1da177e4SLinus Torvalds 	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
43*1da177e4SLinus Torvalds 	hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
44*1da177e4SLinus Torvalds again:
45*1da177e4SLinus Torvalds 	err = hfs_brec_read(&fd, &entry, sizeof(entry));
46*1da177e4SLinus Torvalds 	if (err) {
47*1da177e4SLinus Torvalds 		if (err == -ENOENT) {
48*1da177e4SLinus Torvalds 			hfs_find_exit(&fd);
49*1da177e4SLinus Torvalds 			/* No such entry */
50*1da177e4SLinus Torvalds 			inode = NULL;
51*1da177e4SLinus Torvalds 			goto out;
52*1da177e4SLinus Torvalds 		}
53*1da177e4SLinus Torvalds 		goto fail;
54*1da177e4SLinus Torvalds 	}
55*1da177e4SLinus Torvalds 	type = be16_to_cpu(entry.type);
56*1da177e4SLinus Torvalds 	if (type == HFSPLUS_FOLDER) {
57*1da177e4SLinus Torvalds 		if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
58*1da177e4SLinus Torvalds 			err = -EIO;
59*1da177e4SLinus Torvalds 			goto fail;
60*1da177e4SLinus Torvalds 		}
61*1da177e4SLinus Torvalds 		cnid = be32_to_cpu(entry.folder.id);
62*1da177e4SLinus Torvalds 		dentry->d_fsdata = (void *)(unsigned long)cnid;
63*1da177e4SLinus Torvalds 	} else if (type == HFSPLUS_FILE) {
64*1da177e4SLinus Torvalds 		if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
65*1da177e4SLinus Torvalds 			err = -EIO;
66*1da177e4SLinus Torvalds 			goto fail;
67*1da177e4SLinus Torvalds 		}
68*1da177e4SLinus Torvalds 		cnid = be32_to_cpu(entry.file.id);
69*1da177e4SLinus Torvalds 		if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
70*1da177e4SLinus Torvalds 		    entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) {
71*1da177e4SLinus Torvalds 			struct qstr str;
72*1da177e4SLinus Torvalds 			char name[32];
73*1da177e4SLinus Torvalds 
74*1da177e4SLinus Torvalds 			if (dentry->d_fsdata) {
75*1da177e4SLinus Torvalds 				err = -ENOENT;
76*1da177e4SLinus Torvalds 				inode = NULL;
77*1da177e4SLinus Torvalds 				goto out;
78*1da177e4SLinus Torvalds 			}
79*1da177e4SLinus Torvalds 			dentry->d_fsdata = (void *)(unsigned long)cnid;
80*1da177e4SLinus Torvalds 			linkid = be32_to_cpu(entry.file.permissions.dev);
81*1da177e4SLinus Torvalds 			str.len = sprintf(name, "iNode%d", linkid);
82*1da177e4SLinus Torvalds 			str.name = name;
83*1da177e4SLinus Torvalds 			hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str);
84*1da177e4SLinus Torvalds 			goto again;
85*1da177e4SLinus Torvalds 		} else if (!dentry->d_fsdata)
86*1da177e4SLinus Torvalds 			dentry->d_fsdata = (void *)(unsigned long)cnid;
87*1da177e4SLinus Torvalds 	} else {
88*1da177e4SLinus Torvalds 		printk("HFS+-fs: Illegal catalog entry type in lookup\n");
89*1da177e4SLinus Torvalds 		err = -EIO;
90*1da177e4SLinus Torvalds 		goto fail;
91*1da177e4SLinus Torvalds 	}
92*1da177e4SLinus Torvalds 	hfs_find_exit(&fd);
93*1da177e4SLinus Torvalds 	inode = iget(dir->i_sb, cnid);
94*1da177e4SLinus Torvalds 	if (!inode)
95*1da177e4SLinus Torvalds 		return ERR_PTR(-EACCES);
96*1da177e4SLinus Torvalds 	if (S_ISREG(inode->i_mode))
97*1da177e4SLinus Torvalds 		HFSPLUS_I(inode).dev = linkid;
98*1da177e4SLinus Torvalds out:
99*1da177e4SLinus Torvalds 	d_add(dentry, inode);
100*1da177e4SLinus Torvalds 	return NULL;
101*1da177e4SLinus Torvalds fail:
102*1da177e4SLinus Torvalds 	hfs_find_exit(&fd);
103*1da177e4SLinus Torvalds 	return ERR_PTR(err);
104*1da177e4SLinus Torvalds }
105*1da177e4SLinus Torvalds 
106*1da177e4SLinus Torvalds static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
107*1da177e4SLinus Torvalds {
108*1da177e4SLinus Torvalds 	struct inode *inode = filp->f_dentry->d_inode;
109*1da177e4SLinus Torvalds 	struct super_block *sb = inode->i_sb;
110*1da177e4SLinus Torvalds 	int len, err;
111*1da177e4SLinus Torvalds 	char strbuf[HFSPLUS_MAX_STRLEN + 1];
112*1da177e4SLinus Torvalds 	hfsplus_cat_entry entry;
113*1da177e4SLinus Torvalds 	struct hfs_find_data fd;
114*1da177e4SLinus Torvalds 	struct hfsplus_readdir_data *rd;
115*1da177e4SLinus Torvalds 	u16 type;
116*1da177e4SLinus Torvalds 
117*1da177e4SLinus Torvalds 	if (filp->f_pos >= inode->i_size)
118*1da177e4SLinus Torvalds 		return 0;
119*1da177e4SLinus Torvalds 
120*1da177e4SLinus Torvalds 	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
121*1da177e4SLinus Torvalds 	hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
122*1da177e4SLinus Torvalds 	err = hfs_brec_find(&fd);
123*1da177e4SLinus Torvalds 	if (err)
124*1da177e4SLinus Torvalds 		goto out;
125*1da177e4SLinus Torvalds 
126*1da177e4SLinus Torvalds 	switch ((u32)filp->f_pos) {
127*1da177e4SLinus Torvalds 	case 0:
128*1da177e4SLinus Torvalds 		/* This is completely artificial... */
129*1da177e4SLinus Torvalds 		if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
130*1da177e4SLinus Torvalds 			goto out;
131*1da177e4SLinus Torvalds 		filp->f_pos++;
132*1da177e4SLinus Torvalds 		/* fall through */
133*1da177e4SLinus Torvalds 	case 1:
134*1da177e4SLinus Torvalds 		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
135*1da177e4SLinus Torvalds 		if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
136*1da177e4SLinus Torvalds 			printk("HFS+-fs: bad catalog folder thread\n");
137*1da177e4SLinus Torvalds 			err = -EIO;
138*1da177e4SLinus Torvalds 			goto out;
139*1da177e4SLinus Torvalds 		}
140*1da177e4SLinus Torvalds 		if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
141*1da177e4SLinus Torvalds 			printk("HFS+-fs: truncated catalog thread\n");
142*1da177e4SLinus Torvalds 			err = -EIO;
143*1da177e4SLinus Torvalds 			goto out;
144*1da177e4SLinus Torvalds 		}
145*1da177e4SLinus Torvalds 		if (filldir(dirent, "..", 2, 1,
146*1da177e4SLinus Torvalds 			    be32_to_cpu(entry.thread.parentID), DT_DIR))
147*1da177e4SLinus Torvalds 			goto out;
148*1da177e4SLinus Torvalds 		filp->f_pos++;
149*1da177e4SLinus Torvalds 		/* fall through */
150*1da177e4SLinus Torvalds 	default:
151*1da177e4SLinus Torvalds 		if (filp->f_pos >= inode->i_size)
152*1da177e4SLinus Torvalds 			goto out;
153*1da177e4SLinus Torvalds 		err = hfs_brec_goto(&fd, filp->f_pos - 1);
154*1da177e4SLinus Torvalds 		if (err)
155*1da177e4SLinus Torvalds 			goto out;
156*1da177e4SLinus Torvalds 	}
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds 	for (;;) {
159*1da177e4SLinus Torvalds 		if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
160*1da177e4SLinus Torvalds 			printk("HFS+-fs: walked past end of dir\n");
161*1da177e4SLinus Torvalds 			err = -EIO;
162*1da177e4SLinus Torvalds 			goto out;
163*1da177e4SLinus Torvalds 		}
164*1da177e4SLinus Torvalds 		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
165*1da177e4SLinus Torvalds 		type = be16_to_cpu(entry.type);
166*1da177e4SLinus Torvalds 		len = HFSPLUS_MAX_STRLEN;
167*1da177e4SLinus Torvalds 		err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
168*1da177e4SLinus Torvalds 		if (err)
169*1da177e4SLinus Torvalds 			goto out;
170*1da177e4SLinus Torvalds 		if (type == HFSPLUS_FOLDER) {
171*1da177e4SLinus Torvalds 			if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
172*1da177e4SLinus Torvalds 				printk("HFS+-fs: small dir entry\n");
173*1da177e4SLinus Torvalds 				err = -EIO;
174*1da177e4SLinus Torvalds 				goto out;
175*1da177e4SLinus Torvalds 			}
176*1da177e4SLinus Torvalds 			if (HFSPLUS_SB(sb).hidden_dir &&
177*1da177e4SLinus Torvalds 			    HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id))
178*1da177e4SLinus Torvalds 				goto next;
179*1da177e4SLinus Torvalds 			if (filldir(dirent, strbuf, len, filp->f_pos,
180*1da177e4SLinus Torvalds 				    be32_to_cpu(entry.folder.id), DT_DIR))
181*1da177e4SLinus Torvalds 				break;
182*1da177e4SLinus Torvalds 		} else if (type == HFSPLUS_FILE) {
183*1da177e4SLinus Torvalds 			if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
184*1da177e4SLinus Torvalds 				printk("HFS+-fs: small file entry\n");
185*1da177e4SLinus Torvalds 				err = -EIO;
186*1da177e4SLinus Torvalds 				goto out;
187*1da177e4SLinus Torvalds 			}
188*1da177e4SLinus Torvalds 			if (filldir(dirent, strbuf, len, filp->f_pos,
189*1da177e4SLinus Torvalds 				    be32_to_cpu(entry.file.id), DT_REG))
190*1da177e4SLinus Torvalds 				break;
191*1da177e4SLinus Torvalds 		} else {
192*1da177e4SLinus Torvalds 			printk("HFS+-fs: bad catalog entry type\n");
193*1da177e4SLinus Torvalds 			err = -EIO;
194*1da177e4SLinus Torvalds 			goto out;
195*1da177e4SLinus Torvalds 		}
196*1da177e4SLinus Torvalds 	next:
197*1da177e4SLinus Torvalds 		filp->f_pos++;
198*1da177e4SLinus Torvalds 		if (filp->f_pos >= inode->i_size)
199*1da177e4SLinus Torvalds 			goto out;
200*1da177e4SLinus Torvalds 		err = hfs_brec_goto(&fd, 1);
201*1da177e4SLinus Torvalds 		if (err)
202*1da177e4SLinus Torvalds 			goto out;
203*1da177e4SLinus Torvalds 	}
204*1da177e4SLinus Torvalds 	rd = filp->private_data;
205*1da177e4SLinus Torvalds 	if (!rd) {
206*1da177e4SLinus Torvalds 		rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
207*1da177e4SLinus Torvalds 		if (!rd) {
208*1da177e4SLinus Torvalds 			err = -ENOMEM;
209*1da177e4SLinus Torvalds 			goto out;
210*1da177e4SLinus Torvalds 		}
211*1da177e4SLinus Torvalds 		filp->private_data = rd;
212*1da177e4SLinus Torvalds 		rd->file = filp;
213*1da177e4SLinus Torvalds 		list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
214*1da177e4SLinus Torvalds 	}
215*1da177e4SLinus Torvalds 	memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
216*1da177e4SLinus Torvalds out:
217*1da177e4SLinus Torvalds 	hfs_find_exit(&fd);
218*1da177e4SLinus Torvalds 	return err;
219*1da177e4SLinus Torvalds }
220*1da177e4SLinus Torvalds 
221*1da177e4SLinus Torvalds static int hfsplus_dir_release(struct inode *inode, struct file *file)
222*1da177e4SLinus Torvalds {
223*1da177e4SLinus Torvalds 	struct hfsplus_readdir_data *rd = file->private_data;
224*1da177e4SLinus Torvalds 	if (rd) {
225*1da177e4SLinus Torvalds 		list_del(&rd->list);
226*1da177e4SLinus Torvalds 		kfree(rd);
227*1da177e4SLinus Torvalds 	}
228*1da177e4SLinus Torvalds 	return 0;
229*1da177e4SLinus Torvalds }
230*1da177e4SLinus Torvalds 
231*1da177e4SLinus Torvalds static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
232*1da177e4SLinus Torvalds 			  struct nameidata *nd)
233*1da177e4SLinus Torvalds {
234*1da177e4SLinus Torvalds 	struct inode *inode;
235*1da177e4SLinus Torvalds 	int res;
236*1da177e4SLinus Torvalds 
237*1da177e4SLinus Torvalds 	inode = hfsplus_new_inode(dir->i_sb, mode);
238*1da177e4SLinus Torvalds 	if (!inode)
239*1da177e4SLinus Torvalds 		return -ENOSPC;
240*1da177e4SLinus Torvalds 
241*1da177e4SLinus Torvalds 	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
242*1da177e4SLinus Torvalds 	if (res) {
243*1da177e4SLinus Torvalds 		inode->i_nlink = 0;
244*1da177e4SLinus Torvalds 		hfsplus_delete_inode(inode);
245*1da177e4SLinus Torvalds 		iput(inode);
246*1da177e4SLinus Torvalds 		return res;
247*1da177e4SLinus Torvalds 	}
248*1da177e4SLinus Torvalds 	hfsplus_instantiate(dentry, inode, inode->i_ino);
249*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
250*1da177e4SLinus Torvalds 	return 0;
251*1da177e4SLinus Torvalds }
252*1da177e4SLinus Torvalds 
253*1da177e4SLinus Torvalds static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
254*1da177e4SLinus Torvalds 			struct dentry *dst_dentry)
255*1da177e4SLinus Torvalds {
256*1da177e4SLinus Torvalds 	struct super_block *sb = dst_dir->i_sb;
257*1da177e4SLinus Torvalds 	struct inode *inode = src_dentry->d_inode;
258*1da177e4SLinus Torvalds 	struct inode *src_dir = src_dentry->d_parent->d_inode;
259*1da177e4SLinus Torvalds 	struct qstr str;
260*1da177e4SLinus Torvalds 	char name[32];
261*1da177e4SLinus Torvalds 	u32 cnid, id;
262*1da177e4SLinus Torvalds 	int res;
263*1da177e4SLinus Torvalds 
264*1da177e4SLinus Torvalds 	if (HFSPLUS_IS_RSRC(inode))
265*1da177e4SLinus Torvalds 		return -EPERM;
266*1da177e4SLinus Torvalds 
267*1da177e4SLinus Torvalds 	if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
268*1da177e4SLinus Torvalds 		for (;;) {
269*1da177e4SLinus Torvalds 			get_random_bytes(&id, sizeof(cnid));
270*1da177e4SLinus Torvalds 			id &= 0x3fffffff;
271*1da177e4SLinus Torvalds 			str.name = name;
272*1da177e4SLinus Torvalds 			str.len = sprintf(name, "iNode%d", id);
273*1da177e4SLinus Torvalds 			res = hfsplus_rename_cat(inode->i_ino,
274*1da177e4SLinus Torvalds 						 src_dir, &src_dentry->d_name,
275*1da177e4SLinus Torvalds 						 HFSPLUS_SB(sb).hidden_dir, &str);
276*1da177e4SLinus Torvalds 			if (!res)
277*1da177e4SLinus Torvalds 				break;
278*1da177e4SLinus Torvalds 			if (res != -EEXIST)
279*1da177e4SLinus Torvalds 				return res;
280*1da177e4SLinus Torvalds 		}
281*1da177e4SLinus Torvalds 		HFSPLUS_I(inode).dev = id;
282*1da177e4SLinus Torvalds 		cnid = HFSPLUS_SB(sb).next_cnid++;
283*1da177e4SLinus Torvalds 		src_dentry->d_fsdata = (void *)(unsigned long)cnid;
284*1da177e4SLinus Torvalds 		res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
285*1da177e4SLinus Torvalds 		if (res)
286*1da177e4SLinus Torvalds 			/* panic? */
287*1da177e4SLinus Torvalds 			return res;
288*1da177e4SLinus Torvalds 		HFSPLUS_SB(sb).file_count++;
289*1da177e4SLinus Torvalds 	}
290*1da177e4SLinus Torvalds 	cnid = HFSPLUS_SB(sb).next_cnid++;
291*1da177e4SLinus Torvalds 	res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
292*1da177e4SLinus Torvalds 	if (res)
293*1da177e4SLinus Torvalds 		return res;
294*1da177e4SLinus Torvalds 
295*1da177e4SLinus Torvalds 	inode->i_nlink++;
296*1da177e4SLinus Torvalds 	hfsplus_instantiate(dst_dentry, inode, cnid);
297*1da177e4SLinus Torvalds 	atomic_inc(&inode->i_count);
298*1da177e4SLinus Torvalds 	inode->i_ctime = CURRENT_TIME_SEC;
299*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
300*1da177e4SLinus Torvalds 	HFSPLUS_SB(sb).file_count++;
301*1da177e4SLinus Torvalds 	sb->s_dirt = 1;
302*1da177e4SLinus Torvalds 
303*1da177e4SLinus Torvalds 	return 0;
304*1da177e4SLinus Torvalds }
305*1da177e4SLinus Torvalds 
306*1da177e4SLinus Torvalds static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
307*1da177e4SLinus Torvalds {
308*1da177e4SLinus Torvalds 	struct super_block *sb = dir->i_sb;
309*1da177e4SLinus Torvalds 	struct inode *inode = dentry->d_inode;
310*1da177e4SLinus Torvalds 	struct qstr str;
311*1da177e4SLinus Torvalds 	char name[32];
312*1da177e4SLinus Torvalds 	u32 cnid;
313*1da177e4SLinus Torvalds 	int res;
314*1da177e4SLinus Torvalds 
315*1da177e4SLinus Torvalds 	if (HFSPLUS_IS_RSRC(inode))
316*1da177e4SLinus Torvalds 		return -EPERM;
317*1da177e4SLinus Torvalds 
318*1da177e4SLinus Torvalds 	cnid = (u32)(unsigned long)dentry->d_fsdata;
319*1da177e4SLinus Torvalds 	if (inode->i_ino == cnid &&
320*1da177e4SLinus Torvalds 	    atomic_read(&HFSPLUS_I(inode).opencnt)) {
321*1da177e4SLinus Torvalds 		str.name = name;
322*1da177e4SLinus Torvalds 		str.len = sprintf(name, "temp%lu", inode->i_ino);
323*1da177e4SLinus Torvalds 		res = hfsplus_rename_cat(inode->i_ino,
324*1da177e4SLinus Torvalds 					 dir, &dentry->d_name,
325*1da177e4SLinus Torvalds 					 HFSPLUS_SB(sb).hidden_dir, &str);
326*1da177e4SLinus Torvalds 		if (!res)
327*1da177e4SLinus Torvalds 			inode->i_flags |= S_DEAD;
328*1da177e4SLinus Torvalds 		return res;
329*1da177e4SLinus Torvalds 	}
330*1da177e4SLinus Torvalds 	res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
331*1da177e4SLinus Torvalds 	if (res)
332*1da177e4SLinus Torvalds 		return res;
333*1da177e4SLinus Torvalds 
334*1da177e4SLinus Torvalds 	inode->i_nlink--;
335*1da177e4SLinus Torvalds 	hfsplus_delete_inode(inode);
336*1da177e4SLinus Torvalds 	if (inode->i_ino != cnid && !inode->i_nlink) {
337*1da177e4SLinus Torvalds 		if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
338*1da177e4SLinus Torvalds 			res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
339*1da177e4SLinus Torvalds 			if (!res)
340*1da177e4SLinus Torvalds 				hfsplus_delete_inode(inode);
341*1da177e4SLinus Torvalds 		} else
342*1da177e4SLinus Torvalds 			inode->i_flags |= S_DEAD;
343*1da177e4SLinus Torvalds 	}
344*1da177e4SLinus Torvalds 	inode->i_ctime = CURRENT_TIME_SEC;
345*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
346*1da177e4SLinus Torvalds 
347*1da177e4SLinus Torvalds 	return res;
348*1da177e4SLinus Torvalds }
349*1da177e4SLinus Torvalds 
350*1da177e4SLinus Torvalds static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
351*1da177e4SLinus Torvalds {
352*1da177e4SLinus Torvalds 	struct inode *inode;
353*1da177e4SLinus Torvalds 	int res;
354*1da177e4SLinus Torvalds 
355*1da177e4SLinus Torvalds 	inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
356*1da177e4SLinus Torvalds 	if (!inode)
357*1da177e4SLinus Torvalds 		return -ENOSPC;
358*1da177e4SLinus Torvalds 
359*1da177e4SLinus Torvalds 	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
360*1da177e4SLinus Torvalds 	if (res) {
361*1da177e4SLinus Torvalds 		inode->i_nlink = 0;
362*1da177e4SLinus Torvalds 		hfsplus_delete_inode(inode);
363*1da177e4SLinus Torvalds 		iput(inode);
364*1da177e4SLinus Torvalds 		return res;
365*1da177e4SLinus Torvalds 	}
366*1da177e4SLinus Torvalds 	hfsplus_instantiate(dentry, inode, inode->i_ino);
367*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
368*1da177e4SLinus Torvalds 	return 0;
369*1da177e4SLinus Torvalds }
370*1da177e4SLinus Torvalds 
371*1da177e4SLinus Torvalds static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
372*1da177e4SLinus Torvalds {
373*1da177e4SLinus Torvalds 	struct inode *inode;
374*1da177e4SLinus Torvalds 	int res;
375*1da177e4SLinus Torvalds 
376*1da177e4SLinus Torvalds 	inode = dentry->d_inode;
377*1da177e4SLinus Torvalds 	if (inode->i_size != 2)
378*1da177e4SLinus Torvalds 		return -ENOTEMPTY;
379*1da177e4SLinus Torvalds 	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
380*1da177e4SLinus Torvalds 	if (res)
381*1da177e4SLinus Torvalds 		return res;
382*1da177e4SLinus Torvalds 	inode->i_nlink = 0;
383*1da177e4SLinus Torvalds 	inode->i_ctime = CURRENT_TIME_SEC;
384*1da177e4SLinus Torvalds 	hfsplus_delete_inode(inode);
385*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
386*1da177e4SLinus Torvalds 	return 0;
387*1da177e4SLinus Torvalds }
388*1da177e4SLinus Torvalds 
389*1da177e4SLinus Torvalds static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
390*1da177e4SLinus Torvalds 			   const char *symname)
391*1da177e4SLinus Torvalds {
392*1da177e4SLinus Torvalds 	struct super_block *sb;
393*1da177e4SLinus Torvalds 	struct inode *inode;
394*1da177e4SLinus Torvalds 	int res;
395*1da177e4SLinus Torvalds 
396*1da177e4SLinus Torvalds 	sb = dir->i_sb;
397*1da177e4SLinus Torvalds 	inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
398*1da177e4SLinus Torvalds 	if (!inode)
399*1da177e4SLinus Torvalds 		return -ENOSPC;
400*1da177e4SLinus Torvalds 
401*1da177e4SLinus Torvalds 	res = page_symlink(inode, symname, strlen(symname) + 1);
402*1da177e4SLinus Torvalds 	if (res) {
403*1da177e4SLinus Torvalds 		inode->i_nlink = 0;
404*1da177e4SLinus Torvalds 		hfsplus_delete_inode(inode);
405*1da177e4SLinus Torvalds 		iput(inode);
406*1da177e4SLinus Torvalds 		return res;
407*1da177e4SLinus Torvalds 	}
408*1da177e4SLinus Torvalds 
409*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
410*1da177e4SLinus Torvalds 	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
411*1da177e4SLinus Torvalds 
412*1da177e4SLinus Torvalds 	if (!res) {
413*1da177e4SLinus Torvalds 		hfsplus_instantiate(dentry, inode, inode->i_ino);
414*1da177e4SLinus Torvalds 		mark_inode_dirty(inode);
415*1da177e4SLinus Torvalds 	}
416*1da177e4SLinus Torvalds 
417*1da177e4SLinus Torvalds 	return res;
418*1da177e4SLinus Torvalds }
419*1da177e4SLinus Torvalds 
420*1da177e4SLinus Torvalds static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
421*1da177e4SLinus Torvalds 			 int mode, dev_t rdev)
422*1da177e4SLinus Torvalds {
423*1da177e4SLinus Torvalds 	struct super_block *sb;
424*1da177e4SLinus Torvalds 	struct inode *inode;
425*1da177e4SLinus Torvalds 	int res;
426*1da177e4SLinus Torvalds 
427*1da177e4SLinus Torvalds 	sb = dir->i_sb;
428*1da177e4SLinus Torvalds 	inode = hfsplus_new_inode(sb, mode);
429*1da177e4SLinus Torvalds 	if (!inode)
430*1da177e4SLinus Torvalds 		return -ENOSPC;
431*1da177e4SLinus Torvalds 
432*1da177e4SLinus Torvalds 	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
433*1da177e4SLinus Torvalds 	if (res) {
434*1da177e4SLinus Torvalds 		inode->i_nlink = 0;
435*1da177e4SLinus Torvalds 		hfsplus_delete_inode(inode);
436*1da177e4SLinus Torvalds 		iput(inode);
437*1da177e4SLinus Torvalds 		return res;
438*1da177e4SLinus Torvalds 	}
439*1da177e4SLinus Torvalds 	init_special_inode(inode, mode, rdev);
440*1da177e4SLinus Torvalds 	hfsplus_instantiate(dentry, inode, inode->i_ino);
441*1da177e4SLinus Torvalds 	mark_inode_dirty(inode);
442*1da177e4SLinus Torvalds 
443*1da177e4SLinus Torvalds 	return 0;
444*1da177e4SLinus Torvalds }
445*1da177e4SLinus Torvalds 
446*1da177e4SLinus Torvalds static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
447*1da177e4SLinus Torvalds 			  struct inode *new_dir, struct dentry *new_dentry)
448*1da177e4SLinus Torvalds {
449*1da177e4SLinus Torvalds 	int res;
450*1da177e4SLinus Torvalds 
451*1da177e4SLinus Torvalds 	/* Unlink destination if it already exists */
452*1da177e4SLinus Torvalds 	if (new_dentry->d_inode) {
453*1da177e4SLinus Torvalds 		res = hfsplus_unlink(new_dir, new_dentry);
454*1da177e4SLinus Torvalds 		if (res)
455*1da177e4SLinus Torvalds 			return res;
456*1da177e4SLinus Torvalds 	}
457*1da177e4SLinus Torvalds 
458*1da177e4SLinus Torvalds 	res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
459*1da177e4SLinus Torvalds 				 old_dir, &old_dentry->d_name,
460*1da177e4SLinus Torvalds 				 new_dir, &new_dentry->d_name);
461*1da177e4SLinus Torvalds 	if (!res)
462*1da177e4SLinus Torvalds 		new_dentry->d_fsdata = old_dentry->d_fsdata;
463*1da177e4SLinus Torvalds 	return res;
464*1da177e4SLinus Torvalds }
465*1da177e4SLinus Torvalds 
466*1da177e4SLinus Torvalds struct inode_operations hfsplus_dir_inode_operations = {
467*1da177e4SLinus Torvalds 	.lookup		= hfsplus_lookup,
468*1da177e4SLinus Torvalds 	.create		= hfsplus_create,
469*1da177e4SLinus Torvalds 	.link		= hfsplus_link,
470*1da177e4SLinus Torvalds 	.unlink		= hfsplus_unlink,
471*1da177e4SLinus Torvalds 	.mkdir		= hfsplus_mkdir,
472*1da177e4SLinus Torvalds 	.rmdir		= hfsplus_rmdir,
473*1da177e4SLinus Torvalds 	.symlink	= hfsplus_symlink,
474*1da177e4SLinus Torvalds 	.mknod		= hfsplus_mknod,
475*1da177e4SLinus Torvalds 	.rename		= hfsplus_rename,
476*1da177e4SLinus Torvalds };
477*1da177e4SLinus Torvalds 
478*1da177e4SLinus Torvalds struct file_operations hfsplus_dir_operations = {
479*1da177e4SLinus Torvalds 	.read		= generic_read_dir,
480*1da177e4SLinus Torvalds 	.readdir	= hfsplus_readdir,
481*1da177e4SLinus Torvalds 	.ioctl          = hfsplus_ioctl,
482*1da177e4SLinus Torvalds 	.llseek		= generic_file_llseek,
483*1da177e4SLinus Torvalds 	.release	= hfsplus_dir_release,
484*1da177e4SLinus Torvalds };
485