Blame view

fs/hfsplus/dir.c 13.9 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   *  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
14
15
  #include <linux/slab.h>
  #include <linux/random.h>
017f8da43   Hin-Tak Leung   hfsplus: fix wors...
16
  #include <linux/nls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  
  #include "hfsplus_fs.h"
  #include "hfsplus_raw.h"
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
20
  #include "xattr.h"
b4c1107cc   Vyacheslav Dubeyko   hfsplus: integrat...
21
  #include "acl.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
  
  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,
00cd8dd3b   Al Viro   stop passing name...
32
  				     unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
  {
  	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 ...
43

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  	dentry->d_fsdata = NULL;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
45
46
47
  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  	if (err)
  		return ERR_PTR(err);
89ac9b4d3   Sougata Santra   hfsplus: fix long...
48
49
50
51
  	err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
  			&dentry->d_name);
  	if (unlikely(err < 0))
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  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 ...
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) &&
68e787c3c   Ernesto A. Fernández   hfsplus: fix NULL...
81
  				HFSPLUS_SB(sb)->hidden_dir &&
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
82
83
84
85
  				(entry.file.create_date ==
  					HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
  						create_date ||
  				entry.file.create_date ==
2b0143b5c   David Howells   VFS: normal files...
86
  					HFSPLUS_I(d_inode(sb->s_root))->
68e787c3c   Ernesto A. Fernández   hfsplus: fix NULL...
87
  						create_date)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
  			struct qstr str;
  			char name[32];
  
  			if (dentry->d_fsdata) {
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
92
93
94
95
96
97
98
99
  				/*
  				 * 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 ...
100
101
  				linkid =
  					be32_to_cpu(entry.file.permissions.dev);
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
102
103
  				str.len = sprintf(name, "iNode%d", linkid);
  				str.name = name;
89ac9b4d3   Sougata Santra   hfsplus: fix long...
104
  				err = hfsplus_cat_build_key(sb, fd.search_key,
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
105
106
  					HFSPLUS_SB(sb)->hidden_dir->i_ino,
  					&str);
89ac9b4d3   Sougata Santra   hfsplus: fix long...
107
108
  				if (unlikely(err < 0))
  					goto fail;
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
109
  				goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
  		} else if (!dentry->d_fsdata)
  			dentry->d_fsdata = (void *)(unsigned long)cnid;
  	} else {
d61426732   Joe Perches   hfs/hfsplus: conv...
114
115
  		pr_err("invalid catalog entry type in lookup
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
  		err = -EIO;
  		goto fail;
  	}
  	hfs_find_exit(&fd);
635253915   David Howells   iget: stop HFSPLU...
120
121
122
  	inode = hfsplus_iget(dir->i_sb, cnid);
  	if (IS_ERR(inode))
  		return ERR_CAST(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	if (S_ISREG(inode->i_mode))
f6089ff87   Christoph Hellwig   hfsplus: fix link...
124
  		HFSPLUS_I(inode)->linkid = linkid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
131
  out:
  	d_add(dentry, inode);
  	return NULL;
  fail:
  	hfs_find_exit(&fd);
  	return ERR_PTR(err);
  }
e72514e7a   Al Viro   [readdir] convert...
132
  static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  {
e72514e7a   Al Viro   [readdir] convert...
134
  	struct inode *inode = file_inode(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  	struct super_block *sb = inode->i_sb;
  	int len, err;
017f8da43   Hin-Tak Leung   hfsplus: fix wors...
137
  	char *strbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
  	hfsplus_cat_entry entry;
  	struct hfs_find_data fd;
  	struct hfsplus_readdir_data *rd;
  	u16 type;
e72514e7a   Al Viro   [readdir] convert...
142
  	if (file->f_pos >= inode->i_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
144
145
146
  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  	if (err)
  		return err;
017f8da43   Hin-Tak Leung   hfsplus: fix wors...
147
148
149
150
151
  	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
  	if (!strbuf) {
  		err = -ENOMEM;
  		goto out;
  	}
89ac9b4d3   Sougata Santra   hfsplus: fix long...
152
  	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
153
  	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  	if (err)
  		goto out;
e72514e7a   Al Viro   [readdir] convert...
156
  	if (ctx->pos == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  		/* This is completely artificial... */
e72514e7a   Al Viro   [readdir] convert...
158
  		if (!dir_emit_dot(file, ctx))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  			goto out;
e72514e7a   Al Viro   [readdir] convert...
160
161
162
  		ctx->pos = 1;
  	}
  	if (ctx->pos == 1) {
6f24f8928   Greg Kroah-Hartman   hfsplus: Fix pote...
163
164
165
166
  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
  			err = -EIO;
  			goto out;
  		}
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
167
168
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
  			fd.entrylength);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  		if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
d61426732   Joe Perches   hfs/hfsplus: conv...
170
171
  			pr_err("bad catalog folder thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
  			err = -EIO;
  			goto out;
  		}
  		if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
d61426732   Joe Perches   hfs/hfsplus: conv...
176
177
  			pr_err("truncated catalog thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
  			err = -EIO;
  			goto out;
  		}
e72514e7a   Al Viro   [readdir] convert...
181
  		if (!dir_emit(ctx, "..", 2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  			    be32_to_cpu(entry.thread.parentID), DT_DIR))
  			goto out;
e72514e7a   Al Viro   [readdir] convert...
184
  		ctx->pos = 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	}
e72514e7a   Al Viro   [readdir] convert...
186
187
188
189
190
  	if (ctx->pos >= inode->i_size)
  		goto out;
  	err = hfs_brec_goto(&fd, ctx->pos - 1);
  	if (err)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  	for (;;) {
  		if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
d61426732   Joe Perches   hfs/hfsplus: conv...
193
194
  			pr_err("walked past end of dir
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
  			err = -EIO;
  			goto out;
  		}
6f24f8928   Greg Kroah-Hartman   hfsplus: Fix pote...
198
199
200
201
202
  
  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
  			err = -EIO;
  			goto out;
  		}
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
203
204
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
  			fd.entrylength);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  		type = be16_to_cpu(entry.type);
017f8da43   Hin-Tak Leung   hfsplus: fix wors...
206
  		len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
  		err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
  		if (err)
  			goto out;
  		if (type == HFSPLUS_FOLDER) {
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
211
212
  			if (fd.entrylength <
  					sizeof(struct hfsplus_cat_folder)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
213
214
  				pr_err("small dir entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
  				err = -EIO;
  				goto out;
  			}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
218
219
220
  			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
221
  				goto next;
e72514e7a   Al Viro   [readdir] convert...
222
  			if (!dir_emit(ctx, strbuf, len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
  				    be32_to_cpu(entry.folder.id), DT_DIR))
  				break;
  		} else if (type == HFSPLUS_FILE) {
97a62eaef   Sergei Antonov   hfsplus: emit pro...
226
227
  			u16 mode;
  			unsigned type = DT_UNKNOWN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  			if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
229
230
  				pr_err("small file entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
  				err = -EIO;
  				goto out;
  			}
97a62eaef   Sergei Antonov   hfsplus: emit pro...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  
  			mode = be16_to_cpu(entry.file.permissions.mode);
  			if (S_ISREG(mode))
  				type = DT_REG;
  			else if (S_ISLNK(mode))
  				type = DT_LNK;
  			else if (S_ISFIFO(mode))
  				type = DT_FIFO;
  			else if (S_ISCHR(mode))
  				type = DT_CHR;
  			else if (S_ISBLK(mode))
  				type = DT_BLK;
  			else if (S_ISSOCK(mode))
  				type = DT_SOCK;
e72514e7a   Al Viro   [readdir] convert...
248
  			if (!dir_emit(ctx, strbuf, len,
97a62eaef   Sergei Antonov   hfsplus: emit pro...
249
  				      be32_to_cpu(entry.file.id), type))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  				break;
  		} else {
d61426732   Joe Perches   hfs/hfsplus: conv...
252
253
  			pr_err("bad catalog entry type
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
  			err = -EIO;
  			goto out;
  		}
20b7643d8   Anton Salikhmetov   hfsplus: spaces/i...
257
  next:
e72514e7a   Al Viro   [readdir] convert...
258
259
  		ctx->pos++;
  		if (ctx->pos >= inode->i_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
  			goto out;
  		err = hfs_brec_goto(&fd, 1);
  		if (err)
  			goto out;
  	}
e72514e7a   Al Viro   [readdir] convert...
265
  	rd = file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
271
  	if (!rd) {
  		rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
  		if (!rd) {
  			err = -ENOMEM;
  			goto out;
  		}
e72514e7a   Al Viro   [readdir] convert...
272
273
  		file->private_data = rd;
  		rd->file = file;
323ee8fc5   Al Viro   hfsplus: switch t...
274
  		spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
275
  		list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
323ee8fc5   Al Viro   hfsplus: switch t...
276
  		spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  	}
323ee8fc5   Al Viro   hfsplus: switch t...
278
279
280
281
  	/*
  	 * Can be done after the list insertion; exclusion with
  	 * hfsplus_delete_cat() is provided by directory lock.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
  	memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
  out:
017f8da43   Hin-Tak Leung   hfsplus: fix wors...
284
  	kfree(strbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
  	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) {
323ee8fc5   Al Viro   hfsplus: switch t...
293
  		spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  		list_del(&rd->list);
323ee8fc5   Al Viro   hfsplus: switch t...
295
  		spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
  		kfree(rd);
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
  static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
  			struct dentry *dst_dentry)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
303
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
2b0143b5c   David Howells   VFS: normal files...
304
305
  	struct inode *inode = d_inode(src_dentry);
  	struct inode *src_dir = d_inode(src_dentry->d_parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
310
311
312
  	struct qstr str;
  	char name[32];
  	u32 cnid, id;
  	int res;
  
  	if (HFSPLUS_IS_RSRC(inode))
  		return -EPERM;
f6089ff87   Christoph Hellwig   hfsplus: fix link...
313
314
  	if (!S_ISREG(inode->i_mode))
  		return -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315

7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
316
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
322
323
324
  	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...
325
  						 sbi->hidden_dir, &str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
  			if (!res)
  				break;
  			if (res != -EEXIST)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
329
  				goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  		}
f6089ff87   Christoph Hellwig   hfsplus: fix link...
331
  		HFSPLUS_I(inode)->linkid = id;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
332
  		cnid = sbi->next_cnid++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  		src_dentry->d_fsdata = (void *)(unsigned long)cnid;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
334
335
  		res = hfsplus_create_cat(cnid, src_dir,
  			&src_dentry->d_name, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  		if (res)
  			/* panic? */
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
338
  			goto out;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
339
  		sbi->file_count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
341
  	cnid = sbi->next_cnid++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
  	res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
344
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

d8c76e6f4   Dave Hansen   [PATCH] r/o bind ...
346
  	inc_nlink(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  	hfsplus_instantiate(dst_dentry, inode, cnid);
7de9c6ee3   Al Viro   new helper: ihold()
348
  	ihold(inode);
02027d42c   Deepa Dinamani   fs: Replace CURRE...
349
  	inode->i_ctime = current_time(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  	mark_inode_dirty(inode);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
351
  	sbi->file_count++;
9e6c5829b   Artem Bityutskiy   hfsplus: get rid ...
352
  	hfsplus_mark_mdb_dirty(dst_dir->i_sb);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
353
354
355
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
  }
  
  static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
360
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
2b0143b5c   David Howells   VFS: normal files...
361
  	struct inode *inode = d_inode(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
364
365
366
367
368
  	struct qstr str;
  	char name[32];
  	u32 cnid;
  	int res;
  
  	if (HFSPLUS_IS_RSRC(inode))
  		return -EPERM;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
369
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
  	cnid = (u32)(unsigned long)dentry->d_fsdata;
  	if (inode->i_ino == cnid &&
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
372
  	    atomic_read(&HFSPLUS_I(inode)->opencnt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
  		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...
377
  					 sbi->hidden_dir, &str);
85b8fe8cc   Christoph Hellwig   hfsplus: free spa...
378
  		if (!res) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  			inode->i_flags |= S_DEAD;
85b8fe8cc   Christoph Hellwig   hfsplus: free spa...
380
381
  			drop_nlink(inode);
  		}
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
382
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
  	}
  	res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
386
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387

af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
388
  	if (inode->i_nlink > 0)
9a53c3a78   Dave Hansen   [PATCH] r/o bind ...
389
  		drop_nlink(inode);
76b0c26af   Roman Zippel   HFS+: fix unlink ...
390
391
392
393
  	if (inode->i_ino == cnid)
  		clear_nlink(inode);
  	if (!inode->i_nlink) {
  		if (inode->i_ino != cnid) {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
394
  			sbi->file_count--;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
395
  			if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
76b0c26af   Roman Zippel   HFS+: fix unlink ...
396
  				res = hfsplus_delete_cat(inode->i_ino,
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
397
  							 sbi->hidden_dir,
76b0c26af   Roman Zippel   HFS+: fix unlink ...
398
399
400
401
402
  							 NULL);
  				if (!res)
  					hfsplus_delete_inode(inode);
  			} else
  				inode->i_flags |= S_DEAD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  		} else
76b0c26af   Roman Zippel   HFS+: fix unlink ...
404
  			hfsplus_delete_inode(inode);
af8c85bb6   Roman Zippel   [PATCH] hfs: set ...
405
  	} else
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
406
  		sbi->file_count--;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
407
  	inode->i_ctime = current_time(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
409
410
  out:
  	mutex_unlock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
415
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
2b0143b5c   David Howells   VFS: normal files...
416
  	struct inode *inode = d_inode(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  	if (inode->i_size != 2)
  		return -ENOTEMPTY;
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
420
421
  
  	mutex_lock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
  	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
  	if (res)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
424
  		goto out;
ce71ec368   Dave Hansen   [PATCH] r/o bind ...
425
  	clear_nlink(inode);
02027d42c   Deepa Dinamani   fs: Replace CURRE...
426
  	inode->i_ctime = current_time(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
  	hfsplus_delete_inode(inode);
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
429
430
431
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
434
435
436
  }
  
  static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
  			   const char *symname)
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
437
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  	struct inode *inode;
27a4e3884   Chengyu Song   hfsplus: incorrec...
439
  	int res = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440

7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
441
  	mutex_lock(&sbi->vh_mutex);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
442
  	inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  	if (!inode)
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
444
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
  
  	res = page_symlink(inode, symname, strlen(symname) + 1);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
447
448
  	if (res)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449

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

324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
454
455
456
457
458
459
460
461
  	res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
  	if (res == -EOPNOTSUPP)
  		res = 0; /* Operation is not supported. */
  	else if (res) {
  		/* Try to delete anyway without error analysis. */
  		hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
  		goto out_err;
  	}
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
462
463
  	hfsplus_instantiate(dentry, inode, inode->i_ino);
  	mark_inode_dirty(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
464
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465

f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
466
  out_err:
6d6b77f16   Miklos Szeredi   filesystems: add ...
467
  	clear_nlink(inode);
f17c89bfc   Christoph Hellwig   hfsplus: fix erro...
468
469
  	hfsplus_delete_inode(inode);
  	iput(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
470
471
  out:
  	mutex_unlock(&sbi->vh_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
  	return res;
  }
  
  static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
1a67aafb5   Al Viro   switch ->mknod() ...
476
  			 umode_t mode, dev_t rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
  {
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
478
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
  	struct inode *inode;
27a4e3884   Chengyu Song   hfsplus: incorrec...
480
  	int res = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481

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

90e616905   Christoph Hellwig   hfsplus: create c...
487
488
  	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
489
  	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
490
491
492
493
494
495
496
497
498
499
  	if (res)
  		goto failed_mknod;
  
  	res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
  	if (res == -EOPNOTSUPP)
  		res = 0; /* Operation is not supported. */
  	else if (res) {
  		/* Try to delete anyway without error analysis. */
  		hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
  		goto failed_mknod;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
  	}
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
501

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  	hfsplus_instantiate(dentry, inode, inode->i_ino);
  	mark_inode_dirty(inode);
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
504
505
506
507
508
509
  	goto out;
  
  failed_mknod:
  	clear_nlink(inode);
  	hfsplus_delete_inode(inode);
  	iput(inode);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
510
511
512
  out:
  	mutex_unlock(&sbi->vh_mutex);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  }
4acdaf27e   Al Viro   switch ->create()...
514
  static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
ebfc3b49a   Al Viro   don't pass nameid...
515
  			  bool excl)
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
516
517
518
  {
  	return hfsplus_mknod(dir, dentry, mode, 0);
  }
18bb1db3e   Al Viro   switch vfs_mkdir(...
519
  static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
30d3abbec   Christoph Hellwig   hfsplus: merge mk...
520
521
522
  {
  	return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
f03b8ad8d   Miklos Szeredi   fs: support RENAM...
524
525
  			  struct inode *new_dir, struct dentry *new_dentry,
  			  unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
  {
  	int res;
f03b8ad8d   Miklos Szeredi   fs: support RENAM...
528
529
  	if (flags & ~RENAME_NOREPLACE)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  	/* Unlink destination if it already exists */
2b0143b5c   David Howells   VFS: normal files...
531
  	if (d_really_is_positive(new_dentry)) {
e36cb0b89   David Howells   VFS: (Scripted) C...
532
  		if (d_is_dir(new_dentry))
40de9a7ce   Christoph Hellwig   hfsplus: fix rena...
533
  			res = hfsplus_rmdir(new_dir, new_dentry);
e3911785b   Sage Weil   hfsplus: remove u...
534
  		else
40de9a7ce   Christoph Hellwig   hfsplus: fix rena...
535
  			res = hfsplus_unlink(new_dir, new_dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
538
539
540
541
542
543
544
545
546
  		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...
547
  const struct inode_operations hfsplus_dir_inode_operations = {
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
548
549
550
551
552
553
554
555
556
  	.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,
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
557
  	.listxattr		= hfsplus_listxattr,
b4c1107cc   Vyacheslav Dubeyko   hfsplus: integrat...
558
559
  #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
  	.get_acl		= hfsplus_get_posix_acl,
b0a7ab570   Christoph Hellwig   hfsplus: use gene...
560
  	.set_acl		= hfsplus_set_posix_acl,
b4c1107cc   Vyacheslav Dubeyko   hfsplus: integrat...
561
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  };
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
563
  const struct file_operations hfsplus_dir_operations = {
eb29d66d4   Christoph Hellwig   hfsplus: write up...
564
  	.fsync		= hfsplus_file_fsync,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  	.read		= generic_read_dir,
323ee8fc5   Al Viro   hfsplus: switch t...
566
  	.iterate_shared	= hfsplus_readdir,
7cc4bcc6f   Arnd Bergmann   hfsplus: Push dow...
567
  	.unlocked_ioctl = hfsplus_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
  	.llseek		= generic_file_llseek,
  	.release	= hfsplus_dir_release,
  };