Blame view

fs/hfsplus/dir.c 12.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   *  linux/fs/hfsplus/dir.c
   *
   * Copyright (C) 2001
   * Brad Boyer (flar@allandria.com)
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   *
   * Handling of directories
   */
  
  #include <linux/errno.h>
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/slab.h>
  #include <linux/random.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  
  #include "hfsplus_fs.h"
  #include "hfsplus_raw.h"
  
  static inline void hfsplus_instantiate(struct dentry *dentry,
  				       struct inode *inode, u32 cnid)
  {
  	dentry->d_fsdata = (void *)(unsigned long)cnid;
  	d_instantiate(dentry, inode);
  }
  
  /* Find the entry inside dir named dentry->d_name */
  static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
  				     struct nameidata *nd)
  {
  	struct inode *inode = NULL;
  	struct hfs_find_data fd;
  	struct super_block *sb;
  	hfsplus_cat_entry entry;
  	int err;
  	u32 cnid, linkid = 0;
  	u16 type;
  
  	sb = dir->i_sb;
d45bce8fa   Duane Griffin   HFS+: add custom ...
39

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  	dentry->d_fsdata = NULL;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
41
42
43
  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  	if (err)
  		return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  	hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
  again:
  	err = hfs_brec_read(&fd, &entry, sizeof(entry));
  	if (err) {
  		if (err == -ENOENT) {
  			hfs_find_exit(&fd);
  			/* No such entry */
  			inode = NULL;
  			goto out;
  		}
  		goto fail;
  	}
  	type = be16_to_cpu(entry.type);
  	if (type == HFSPLUS_FOLDER) {
  		if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
  			err = -EIO;
  			goto fail;
  		}
  		cnid = be32_to_cpu(entry.folder.id);
  		dentry->d_fsdata = (void *)(unsigned long)cnid;
  	} else if (type == HFSPLUS_FILE) {
  		if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
  			err = -EIO;
  			goto fail;
  		}
  		cnid = be32_to_cpu(entry.file.id);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
70
71
72
73
74
75
76
77
78
79
80
  		if (entry.file.user_info.fdType ==
  				cpu_to_be32(HFSP_HARDLINK_TYPE) &&
  				entry.file.user_info.fdCreator ==
  				cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
  				(entry.file.create_date ==
  					HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
  						create_date ||
  				entry.file.create_date ==
  					HFSPLUS_I(sb->s_root->d_inode)->
  						create_date) &&
  				HFSPLUS_SB(sb)->hidden_dir) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
  			struct qstr str;
  			char name[32];
  
  			if (dentry->d_fsdata) {
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
85
86
87
88
89
90
91
92
  				/*
  				 * We found a link pointing to another link,
  				 * so ignore it and treat it as regular file.
  				 */
  				cnid = (unsigned long)dentry->d_fsdata;
  				linkid = 0;
  			} else {
  				dentry->d_fsdata = (void *)(unsigned long)cnid;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
93
94
  				linkid =
  					be32_to_cpu(entry.file.permissions.dev);
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
95
96
  				str.len = sprintf(name, "iNode%d", linkid);
  				str.name = name;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
97
  				hfsplus_cat_build_key(sb, fd.search_key,
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
98
99
  					HFSPLUS_SB(sb)->hidden_dir->i_ino,
  					&str);
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
100
  				goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  		} else if (!dentry->d_fsdata)
  			dentry->d_fsdata = (void *)(unsigned long)cnid;
  	} else {
634725a92   Roman Zippel   [PATCH] hfs: clea...
105
106
  		printk(KERN_ERR "hfs: invalid catalog entry type in lookup
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
  		err = -EIO;
  		goto fail;
  	}
  	hfs_find_exit(&fd);
635253915   David Howells   iget: stop HFSPLU...
111
112
113
  	inode = hfsplus_iget(dir->i_sb, cnid);
  	if (IS_ERR(inode))
  		return ERR_CAST(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  	if (S_ISREG(inode->i_mode))
f6089ff87   Christoph Hellwig   hfsplus: fix link...
115
  		HFSPLUS_I(inode)->linkid = linkid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
123
124
125
  out:
  	d_add(dentry, inode);
  	return NULL;
  fail:
  	hfs_find_exit(&fd);
  	return ERR_PTR(err);
  }
  
  static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
  {
f44ea0310   Josef Sipek   [PATCH] struct pa...
126
  	struct inode *inode = filp->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
131
132
133
134
135
136
  	struct super_block *sb = inode->i_sb;
  	int len, err;
  	char strbuf[HFSPLUS_MAX_STRLEN + 1];
  	hfsplus_cat_entry entry;
  	struct hfs_find_data fd;
  	struct hfsplus_readdir_data *rd;
  	u16 type;
  
  	if (filp->f_pos >= inode->i_size)
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
137
138
139
  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
148
149
150
151
152
  	hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
  	err = hfs_brec_find(&fd);
  	if (err)
  		goto out;
  
  	switch ((u32)filp->f_pos) {
  	case 0:
  		/* This is completely artificial... */
  		if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
  			goto out;
  		filp->f_pos++;
  		/* fall through */
  	case 1:
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
153
154
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
  			fd.entrylength);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  		if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
156
157
  			printk(KERN_ERR "hfs: bad catalog folder thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
  			err = -EIO;
  			goto out;
  		}
  		if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
162
163
  			printk(KERN_ERR "hfs: truncated catalog thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  			err = -EIO;
  			goto out;
  		}
  		if (filldir(dirent, "..", 2, 1,
  			    be32_to_cpu(entry.thread.parentID), DT_DIR))
  			goto out;
  		filp->f_pos++;
  		/* fall through */
  	default:
  		if (filp->f_pos >= inode->i_size)
  			goto out;
  		err = hfs_brec_goto(&fd, filp->f_pos - 1);
  		if (err)
  			goto out;
  	}
  
  	for (;;) {
  		if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
182
183
  			printk(KERN_ERR "hfs: walked past end of dir
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
  			err = -EIO;
  			goto out;
  		}
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
187
188
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
  			fd.entrylength);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
  		type = be16_to_cpu(entry.type);
  		len = HFSPLUS_MAX_STRLEN;
  		err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
  		if (err)
  			goto out;
  		if (type == HFSPLUS_FOLDER) {
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
195
196
  			if (fd.entrylength <
  					sizeof(struct hfsplus_cat_folder)) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
197
198
  				printk(KERN_ERR "hfs: small dir entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
  				err = -EIO;
  				goto out;
  			}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
202
203
204
  			if (HFSPLUS_SB(sb)->hidden_dir &&
  			    HFSPLUS_SB(sb)->hidden_dir->i_ino ==
  					be32_to_cpu(entry.folder.id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
  				goto next;
  			if (filldir(dirent, strbuf, len, filp->f_pos,
  				    be32_to_cpu(entry.folder.id), DT_DIR))
  				break;
  		} else if (type == HFSPLUS_FILE) {
  			if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
211
212
  				printk(KERN_ERR "hfs: small file entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
218
219
  				err = -EIO;
  				goto out;
  			}
  			if (filldir(dirent, strbuf, len, filp->f_pos,
  				    be32_to_cpu(entry.file.id), DT_REG))
  				break;
  		} else {
634725a92   Roman Zippel   [PATCH] hfs: clea...
220
221
  			printk(KERN_ERR "hfs: bad catalog entry type
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  			err = -EIO;
  			goto out;
  		}
20b7643d8   Anton Salikhmetov   hfsplus: spaces/i...
225
  next:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  		filp->f_pos++;
  		if (filp->f_pos >= inode->i_size)
  			goto out;
  		err = hfs_brec_goto(&fd, 1);
  		if (err)
  			goto out;
  	}
  	rd = filp->private_data;
  	if (!rd) {
  		rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
  		if (!rd) {
  			err = -ENOMEM;
  			goto out;
  		}
  		filp->private_data = rd;
  		rd->file = filp;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
242
  		list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
  	}
  	memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
  out:
  	hfs_find_exit(&fd);
  	return err;
  }
  
  static int hfsplus_dir_release(struct inode *inode, struct file *file)
  {
  	struct hfsplus_readdir_data *rd = file->private_data;
  	if (rd) {
89755dcac   Christoph Hellwig   hfsplus: protect ...
254
  		mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  		list_del(&rd->list);
89755dcac   Christoph Hellwig   hfsplus: protect ...
256
  		mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
  		kfree(rd);
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
  static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
  			struct dentry *dst_dentry)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
264
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
  	struct inode *inode = src_dentry->d_inode;
  	struct inode *src_dir = src_dentry->d_parent->d_inode;
  	struct qstr str;
  	char name[32];
  	u32 cnid, id;
  	int res;
  
  	if (HFSPLUS_IS_RSRC(inode))
  		return -EPERM;
f6089ff87   Christoph Hellwig   hfsplus: fix link...
274
275
  	if (!S_ISREG(inode->i_mode))
  		return -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276

7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
277
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
282
283
284
285
  	if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
  		for (;;) {
  			get_random_bytes(&id, sizeof(cnid));
  			id &= 0x3fffffff;
  			str.name = name;
  			str.len = sprintf(name, "iNode%d", id);
  			res = hfsplus_rename_cat(inode->i_ino,
  						 src_dir, &src_dentry->d_name,
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
286
  						 sbi->hidden_dir, &str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
  			if (!res)
  				break;
  			if (res != -EEXIST)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
290
  				goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  		}
f6089ff87   Christoph Hellwig   hfsplus: fix link...
292
  		HFSPLUS_I(inode)->linkid = id;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
293
  		cnid = sbi->next_cnid++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  		src_dentry->d_fsdata = (void *)(unsigned long)cnid;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
295
296
  		res = hfsplus_create_cat(cnid, src_dir,
  			&src_dentry->d_name, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
  		if (res)
  			/* panic? */
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
299
  			goto out;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
300
  		sbi->file_count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
302
  	cnid = sbi->next_cnid++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
  	res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
305
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306

d8c76e6f4   Dave Hansen   [PATCH] r/o bind ...
307
  	inc_nlink(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	hfsplus_instantiate(dst_dentry, inode, cnid);
7de9c6ee3   Al Viro   new helper: ihold()
309
  	ihold(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
  	inode->i_ctime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
312
313
  	sbi->file_count++;
  	dst_dir->i_sb->s_dirt = 1;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
314
315
316
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
  }
  
  static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
321
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
328
329
  	struct inode *inode = dentry->d_inode;
  	struct qstr str;
  	char name[32];
  	u32 cnid;
  	int res;
  
  	if (HFSPLUS_IS_RSRC(inode))
  		return -EPERM;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
330
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  	cnid = (u32)(unsigned long)dentry->d_fsdata;
  	if (inode->i_ino == cnid &&
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
333
  	    atomic_read(&HFSPLUS_I(inode)->opencnt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
  		str.name = name;
  		str.len = sprintf(name, "temp%lu", inode->i_ino);
  		res = hfsplus_rename_cat(inode->i_ino,
  					 dir, &dentry->d_name,
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
338
  					 sbi->hidden_dir, &str);
85b8fe8cc   Christoph Hellwig   hfsplus: free spa...
339
  		if (!res) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  			inode->i_flags |= S_DEAD;
85b8fe8cc   Christoph Hellwig   hfsplus: free spa...
341
342
  			drop_nlink(inode);
  		}
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
343
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
  	}
  	res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
347
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348

af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
349
  	if (inode->i_nlink > 0)
9a53c3a78   Dave Hansen   [PATCH] r/o bind ...
350
  		drop_nlink(inode);
76b0c26af   Roman Zippel   HFS+: fix unlink ...
351
352
353
354
  	if (inode->i_ino == cnid)
  		clear_nlink(inode);
  	if (!inode->i_nlink) {
  		if (inode->i_ino != cnid) {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
355
  			sbi->file_count--;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
356
  			if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
76b0c26af   Roman Zippel   HFS+: fix unlink ...
357
  				res = hfsplus_delete_cat(inode->i_ino,
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
358
  							 sbi->hidden_dir,
76b0c26af   Roman Zippel   HFS+: fix unlink ...
359
360
361
362
363
  							 NULL);
  				if (!res)
  					hfsplus_delete_inode(inode);
  			} else
  				inode->i_flags |= S_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  		} else
76b0c26af   Roman Zippel   HFS+: fix unlink ...
365
  			hfsplus_delete_inode(inode);
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
366
  	} else
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
367
  		sbi->file_count--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  	inode->i_ctime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
370
371
  out:
  	mutex_unlock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
  static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
376
377
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
  	struct inode *inode = dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  	if (inode->i_size != 2)
  		return -ENOTEMPTY;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
381
382
  
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
  	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
385
  		goto out;
ce71ec368   Dave Hansen   [PATCH] r/o bind ...
386
  	clear_nlink(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
  	inode->i_ctime = CURRENT_TIME_SEC;
  	hfsplus_delete_inode(inode);
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
390
391
392
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
397
  }
  
  static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
  			   const char *symname)
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
398
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
  	struct inode *inode;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
400
  	int res = -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401

7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
402
  	mutex_lock(&sbi->vh_mutex);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
403
  	inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  	if (!inode)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
405
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
  
  	res = page_symlink(inode, symname, strlen(symname) + 1);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
408
409
  	if (res)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
412
413
  	if (res)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414

f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
415
416
  	hfsplus_instantiate(dentry, inode, inode->i_ino);
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
417
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
419
  out_err:
6d6b77f16   Miklos Szeredi   filesystems: add ...
420
  	clear_nlink(inode);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
421
422
  	hfsplus_delete_inode(inode);
  	iput(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
423
424
  out:
  	mutex_unlock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
  	return res;
  }
  
  static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
1a67aafb5   Al Viro   switch ->mknod() ...
429
  			 umode_t mode, dev_t rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
431
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  	struct inode *inode;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
433
  	int res = -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434

7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
435
  	mutex_lock(&sbi->vh_mutex);
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
436
  	inode = hfsplus_new_inode(dir->i_sb, mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  	if (!inode)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
438
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439

90e616905   Christoph Hellwig   hfsplus: create c...
440
441
  	if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
  		init_special_inode(inode, mode, rdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
  	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
  	if (res) {
6d6b77f16   Miklos Szeredi   filesystems: add ...
444
  		clear_nlink(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
  		hfsplus_delete_inode(inode);
  		iput(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
447
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
  	}
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
449

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
  	hfsplus_instantiate(dentry, inode, inode->i_ino);
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
452
453
454
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  }
4acdaf27e   Al Viro   switch ->create()...
456
  static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
457
458
459
460
  			  struct nameidata *nd)
  {
  	return hfsplus_mknod(dir, dentry, mode, 0);
  }
18bb1db3e   Al Viro   switch vfs_mkdir(...
461
  static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
462
463
464
  {
  	return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
469
470
471
  static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
  			  struct inode *new_dir, struct dentry *new_dentry)
  {
  	int res;
  
  	/* Unlink destination if it already exists */
  	if (new_dentry->d_inode) {
e3911785b   Sage Weil   hfsplus: remove u...
472
  		if (S_ISDIR(new_dentry->d_inode->i_mode))
40de9a7ce   Christoph Hellwig   hfsplus: fix rena...
473
  			res = hfsplus_rmdir(new_dir, new_dentry);
e3911785b   Sage Weil   hfsplus: remove u...
474
  		else
40de9a7ce   Christoph Hellwig   hfsplus: fix rena...
475
  			res = hfsplus_unlink(new_dir, new_dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
484
485
486
  		if (res)
  			return res;
  	}
  
  	res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
  				 old_dir, &old_dentry->d_name,
  				 new_dir, &new_dentry->d_name);
  	if (!res)
  		new_dentry->d_fsdata = old_dentry->d_fsdata;
  	return res;
  }
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
487
  const struct inode_operations hfsplus_dir_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
491
492
493
494
495
496
497
  	.lookup		= hfsplus_lookup,
  	.create		= hfsplus_create,
  	.link		= hfsplus_link,
  	.unlink		= hfsplus_unlink,
  	.mkdir		= hfsplus_mkdir,
  	.rmdir		= hfsplus_rmdir,
  	.symlink	= hfsplus_symlink,
  	.mknod		= hfsplus_mknod,
  	.rename		= hfsplus_rename,
  };
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
498
  const struct file_operations hfsplus_dir_operations = {
eb29d66d4   Christoph Hellwig   hfsplus: write up...
499
  	.fsync		= hfsplus_file_fsync,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
  	.read		= generic_read_dir,
  	.readdir	= hfsplus_readdir,
7cc4bcc6f   Arnd Bergmann   hfsplus: Push dow...
502
  	.unlocked_ioctl = hfsplus_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
  	.llseek		= generic_file_llseek,
  	.release	= hfsplus_dir_release,
  };