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