1 /*
2  * linux/fs/hfs/dir.c
3  *
4  * Copyright (C) 1995-1997  Paul H. Hargrove
5  * This file may be distributed under the terms of the GNU General Public License.
6  *
7  * This file contains directory-related functions independent of which
8  * scheme is being used to represent forks.
9  *
10  * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
11  *
12  * "XXX" in a comment is a note to myself to consider changing something.
13  *
14  * In function preconditions the term "valid" applied to a pointer to
15  * a structure means that the pointer is non-NULL and the structure it
16  * points to has all fields initialized to consistent values.
17  */
18 
19 #include "hfs.h"
20 #include <linux/hfs_fs_sb.h>
21 #include <linux/hfs_fs_i.h>
22 #include <linux/hfs_fs.h>
23 
24 /*================ File-local functions ================*/
25 
26 /*
27  * build_key()
28  *
29  * Build a key for a file by the given name in the given directory.
30  * If the name matches one of the reserved names returns 1 otherwise 0.
31  */
build_key(struct hfs_cat_key * key,struct inode * dir,const char * name,int len)32 static int build_key(struct hfs_cat_key *key, struct inode *dir,
33 		     const char *name, int len)
34 {
35 	struct hfs_name cname;
36 	const struct hfs_name *reserved;
37 
38 	/* mangle the name */
39 	hfs_nameout(dir, &cname, name, len);
40 
41 	/* check against reserved names */
42 	reserved = HFS_SB(dir->i_sb)->s_reserved1;
43 	while (reserved->Len) {
44 		if (hfs_streq(reserved->Name, reserved->Len,
45 			      cname.Name, cname.Len)) {
46 			return 1;
47 		}
48 		++reserved;
49 	}
50 
51 	/* check against the names reserved only in the root directory */
52 	if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
53 		reserved = HFS_SB(dir->i_sb)->s_reserved2;
54 		while (reserved->Len) {
55 			if (hfs_streq(reserved->Name, reserved->Len,
56 				      cname.Name, cname.Len)) {
57 				return 1;
58 			}
59 			++reserved;
60 		}
61 	}
62 
63 	/* build the key */
64 	hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
65 
66 	return 0;
67 }
68 
69 /*
70  * update_dirs_plus()
71  *
72  * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
73  * 'i_version' of the inodes associated with a directory that has
74  * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
75  */
update_dirs_plus(struct hfs_cat_entry * dir,int is_dir)76 static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
77 {
78 	int i;
79 
80 	for (i = 0; i < 4; ++i) {
81 		struct dentry *de = dir->sys_entry[i];
82 		if (de) {
83 		        struct inode *tmp = de->d_inode;
84 			if (S_ISDIR(tmp->i_mode)) {
85 				if (is_dir &&
86 				    (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
87 					/* In "normal" directory only */
88 					++(tmp->i_nlink);
89 				}
90 				tmp->i_size += HFS_I(tmp)->dir_size;
91 				tmp->i_version = ++event;
92 			}
93 			tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
94 			mark_inode_dirty(tmp);
95 		}
96 	}
97 }
98 
99 /*
100  * update_dirs_minus()
101  *
102  * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
103  * 'i_version' of the inodes associated with a directory that has
104  * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
105  */
update_dirs_minus(struct hfs_cat_entry * dir,int is_dir)106 static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
107 {
108 	int i;
109 
110 	for (i = 0; i < 4; ++i) {
111 		struct dentry *de = dir->sys_entry[i];
112 		if (de) {
113 		        struct inode *tmp = de->d_inode;
114 			if (S_ISDIR(tmp->i_mode)) {
115 				if (is_dir &&
116 				    (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
117 					/* In "normal" directory only */
118 					--(tmp->i_nlink);
119 				}
120 				tmp->i_size -= HFS_I(tmp)->dir_size;
121 				tmp->i_version = ++event;
122 			}
123 			tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
124 			mark_inode_dirty(tmp);
125 		}
126 	}
127 }
128 
129 /*
130  * mark_inodes_deleted()
131  *
132  * Update inodes associated with a deleted entry to reflect its deletion.
133  * Well, we really just drop the dentry.
134  *
135  * XXX: we should be using delete_inode for some of this stuff.
136  */
mark_inodes_deleted(struct hfs_cat_entry * entry,struct dentry * dentry)137 static inline void mark_inodes_deleted(struct hfs_cat_entry *entry,
138 				       struct dentry *dentry)
139 {
140 	struct dentry *de;
141 	struct inode *tmp;
142 	int i;
143 
144 	for (i = 0; i < 4; ++i) {
145 		if ((de = entry->sys_entry[i]) && (dentry != de)) {
146 		      dget(de);
147 		      tmp = de->d_inode;
148 		      tmp->i_nlink = 0;
149 		      tmp->i_ctime = CURRENT_TIME;
150 		      mark_inode_dirty(tmp);
151 		      d_delete(de);
152 		      dput(de);
153 		}
154 	}
155 }
156 
157 /*================ Global functions ================*/
158 
159 /*
160  * hfs_create()
161  *
162  * This is the create() entry in the inode_operations structure for
163  * regular HFS directories.  The purpose is to create a new file in
164  * a directory and return a corresponding inode, given the inode for
165  * the directory and the name (and its length) of the new file.
166  */
hfs_create(struct inode * dir,struct dentry * dentry,int mode)167 int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
168 {
169 	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
170 	struct hfs_cat_entry *new;
171 	struct hfs_cat_key key;
172 	struct inode *inode;
173 	int error;
174 
175 	/* build the key, checking against reserved names */
176 	if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len))
177 		return -EEXIST;
178 
179 	if ((error = hfs_cat_create(entry, &key,
180 			       (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
181 			       HFS_SB(dir->i_sb)->s_type,
182 			       HFS_SB(dir->i_sb)->s_creator, &new)))
183 		return error;
184 
185 	/* create an inode for the new file. back out if we run
186 	 * into trouble. */
187 	new->count++; /* hfs_iget() eats one */
188 	if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) {
189 		hfs_cat_delete(entry, new, 1);
190 		hfs_cat_put(new);
191 		return -EIO;
192 	}
193 
194 	hfs_cat_put(new);
195 	update_dirs_plus(entry, 0);
196 	/* toss any relevant negative dentries */
197 	if (HFS_I(dir)->d_drop_op)
198 		HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
199 	mark_inode_dirty(inode);
200 	d_instantiate(dentry, inode);
201 	return 0;
202 }
203 
204 /*
205  * hfs_mkdir()
206  *
207  * This is the mkdir() entry in the inode_operations structure for
208  * regular HFS directories.  The purpose is to create a new directory
209  * in a directory, given the inode for the parent directory and the
210  * name (and its length) of the new directory.
211  */
hfs_mkdir(struct inode * parent,struct dentry * dentry,int mode)212 int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
213 {
214 	struct hfs_cat_entry *entry = HFS_I(parent)->entry;
215 	struct hfs_cat_entry *new;
216 	struct hfs_cat_key key;
217 	struct inode *inode;
218 	int error;
219 
220 	/* build the key, checking against reserved names */
221 	if (build_key(&key, parent, dentry->d_name.name,
222 		      dentry->d_name.len))
223 		return -EEXIST;
224 
225 	/* try to create the directory */
226 	if ((error = hfs_cat_mkdir(entry, &key, &new)))
227 		return error;
228 
229 	/* back out if we run into trouble */
230 	new->count++; /* hfs_iget eats one */
231 	if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) {
232 		hfs_cat_delete(entry, new, 1);
233 		hfs_cat_put(new);
234 		return -EIO;
235 	}
236 
237 	hfs_cat_put(new);
238 	update_dirs_plus(entry, 1);
239 	mark_inode_dirty(inode);
240 	d_instantiate(dentry, inode);
241 	return 0;
242 }
243 
244 /*
245  * hfs_unlink()
246  *
247  * This is the unlink() entry in the inode_operations structure for
248  * regular HFS directories.  The purpose is to delete an existing
249  * file, given the inode for the parent directory and the name
250  * (and its length) of the existing file.
251  */
hfs_unlink(struct inode * dir,struct dentry * dentry)252 int hfs_unlink(struct inode * dir, struct dentry *dentry)
253 {
254 	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
255 	struct hfs_cat_entry *victim = NULL;
256 	struct hfs_cat_key key;
257 	int error;
258 
259 	if (build_key(&key, dir, dentry->d_name.name,
260 		      dentry->d_name.len))
261 		return -EPERM;
262 
263 	if (!(victim = hfs_cat_get(entry->mdb, &key)))
264 		return -ENOENT;
265 
266 	error = -EPERM;
267 	if (victim->type != HFS_CDR_FIL)
268 		goto hfs_unlink_put;
269 
270 	if (!(error = hfs_cat_delete(entry, victim, 1))) {
271 		struct inode *inode = dentry->d_inode;
272 
273 		mark_inodes_deleted(victim, dentry);
274 		inode->i_nlink--;
275 		inode->i_ctime = CURRENT_TIME;
276 		mark_inode_dirty(inode);
277 		update_dirs_minus(entry, 0);
278 	}
279 
280 hfs_unlink_put:
281 	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
282 	return error;
283 }
284 
285 /*
286  * hfs_rmdir()
287  *
288  * This is the rmdir() entry in the inode_operations structure for
289  * regular HFS directories.  The purpose is to delete an existing
290  * directory, given the inode for the parent directory and the name
291  * (and its length) of the existing directory.
292  */
hfs_rmdir(struct inode * parent,struct dentry * dentry)293 int hfs_rmdir(struct inode * parent, struct dentry *dentry)
294 {
295 	struct hfs_cat_entry *entry = HFS_I(parent)->entry;
296 	struct hfs_cat_entry *victim = NULL;
297 	struct inode *inode = dentry->d_inode;
298 	struct hfs_cat_key key;
299 	int error;
300 
301 	if (build_key(&key, parent, dentry->d_name.name,
302 		      dentry->d_name.len))
303 		return -EPERM;
304 
305 	if (!(victim = hfs_cat_get(entry->mdb, &key)))
306 		return -ENOENT;
307 
308 	error = -ENOTDIR;
309 	if (victim->type != HFS_CDR_DIR)
310 		goto hfs_rmdir_put;
311 
312 	error = -EBUSY;
313 	if (!d_unhashed(dentry))
314 		goto hfs_rmdir_put;
315 
316 	/* we only have to worry about 2 and 3 for mount points */
317 	if (victim->sys_entry[2] && d_mountpoint(victim->sys_entry[2]))
318 		goto hfs_rmdir_put;
319 	if (victim->sys_entry[3] && d_mountpoint(victim->sys_entry[3]))
320 		goto hfs_rmdir_put;
321 
322 
323 	if ((error = hfs_cat_delete(entry, victim, 1)))
324 		goto hfs_rmdir_put;
325 
326 	mark_inodes_deleted(victim, dentry);
327 	inode->i_nlink = 0;
328 	inode->i_ctime = CURRENT_TIME;
329 	mark_inode_dirty(inode);
330 	update_dirs_minus(entry, 1);
331 
332 hfs_rmdir_put:
333 	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
334 	return error;
335 }
336 
337 /*
338  * hfs_rename()
339  *
340  * This is the rename() entry in the inode_operations structure for
341  * regular HFS directories.  The purpose is to rename an existing
342  * file or directory, given the inode for the current directory and
343  * the name (and its length) of the existing file/directory and the
344  * inode for the new directory and the name (and its length) of the
345  * new file/directory.
346  * XXX: how do you handle must_be dir?
347  */
hfs_rename(struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry)348 int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
349 	       struct inode *new_dir, struct dentry *new_dentry)
350 {
351 	struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
352 	struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
353 	struct hfs_cat_entry *victim = NULL;
354 	struct hfs_cat_entry *deleted;
355 	struct hfs_cat_key key;
356 	int error;
357 
358 	if (build_key(&key, old_dir, old_dentry->d_name.name,
359 		      old_dentry->d_name.len) ||
360 	    (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino)))
361 		return -EPERM;
362 
363 	if (!(victim = hfs_cat_get(old_parent->mdb, &key)))
364 		return -ENOENT;
365 
366 	error = -EPERM;
367 	if (build_key(&key, new_dir, new_dentry->d_name.name,
368 			     new_dentry->d_name.len))
369 		goto hfs_rename_put;
370 
371 	if (!(error = hfs_cat_move(old_parent, new_parent,
372 				   victim, &key, &deleted))) {
373 		int is_dir = (victim->type == HFS_CDR_DIR);
374 
375 		/* drop the old dentries */
376 		mark_inodes_deleted(victim, old_dentry);
377 		update_dirs_minus(old_parent, is_dir);
378 		if (deleted) {
379 			mark_inodes_deleted(deleted, new_dentry);
380 			hfs_cat_put(deleted);
381 		} else {
382 			/* no existing inodes. just drop negative dentries */
383 			if (HFS_I(new_dir)->d_drop_op)
384 				HFS_I(new_dir)->d_drop_op(new_dentry,
385 					  HFS_I(new_dir)->file_type);
386 			update_dirs_plus(new_parent, is_dir);
387 		}
388 
389 	}
390 
391 hfs_rename_put:
392 	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
393 	return error;
394 }
395