Blame view

fs/sysv/namei.c 6.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   *  linux/fs/sysv/namei.c
   *
   *  minix/namei.c
   *  Copyright (C) 1991, 1992  Linus Torvalds
   *
   *  coh/namei.c
   *  Copyright (C) 1993  Pascal Haible, Bruno Haible
   *
   *  sysv/namei.c
   *  Copyright (C) 1993  Bruno Haible
   *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
   */
  
  #include <linux/pagemap.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include "sysv.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
  static int add_nondir(struct dentry *dentry, struct inode *inode)
  {
  	int err = sysv_add_link(dentry, inode);
  	if (!err) {
  		d_instantiate(dentry, inode);
  		return 0;
  	}
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
24
  	inode_dec_link_count(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
  	iput(inode);
  	return err;
  }
b1e6a015a   Nick Piggin   fs: change d_hash...
28
29
  static int sysv_hash(const struct dentry *dentry, const struct inode *inode,
  		struct qstr *qstr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
  {
  	/* Truncate the name in place, avoids having to define a compare
  	   function. */
  	if (qstr->len > SYSV_NAMELEN) {
  		qstr->len = SYSV_NAMELEN;
  		qstr->hash = full_name_hash(qstr->name, qstr->len);
  	}
  	return 0;
  }
e16404ed0   Al Viro   constify dentry_o...
39
  const struct dentry_operations sysv_dentry_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
  	.d_hash		= sysv_hash,
  };
  
  static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
  {
  	struct inode * inode = NULL;
  	ino_t ino;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
  	if (dentry->d_name.len > SYSV_NAMELEN)
  		return ERR_PTR(-ENAMETOOLONG);
  	ino = sysv_inode_by_name(dentry);
  
  	if (ino) {
b8e1343f6   David Howells   iget: stop the SY...
52
53
54
  		inode = sysv_iget(dir->i_sb, ino);
  		if (IS_ERR(inode))
  			return ERR_CAST(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
  	}
  	d_add(dentry, inode);
  	return NULL;
  }
1a67aafb5   Al Viro   switch ->mknod() ...
59
  static int sysv_mknod(struct inode * dir, struct dentry * dentry, umode_t mode, dev_t rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  {
  	struct inode * inode;
  	int err;
  
  	if (!old_valid_dev(rdev))
  		return -EINVAL;
  
  	inode = sysv_new_inode(dir, mode);
  	err = PTR_ERR(inode);
  
  	if (!IS_ERR(inode)) {
  		sysv_set_inode(inode, rdev);
  		mark_inode_dirty(inode);
  		err = add_nondir(dentry, inode);
  	}
  	return err;
  }
4acdaf27e   Al Viro   switch ->create()...
77
  static int sysv_create(struct inode * dir, struct dentry * dentry, umode_t mode, struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  {
  	return sysv_mknod(dir, dentry, mode, 0);
  }
  
  static int sysv_symlink(struct inode * dir, struct dentry * dentry, 
  	const char * symname)
  {
  	int err = -ENAMETOOLONG;
  	int l = strlen(symname)+1;
  	struct inode * inode;
  
  	if (l > dir->i_sb->s_blocksize)
  		goto out;
  
  	inode = sysv_new_inode(dir, S_IFLNK|0777);
  	err = PTR_ERR(inode);
  	if (IS_ERR(inode))
  		goto out;
  	
  	sysv_set_inode(inode, 0);
  	err = page_symlink(inode, symname, l);
  	if (err)
  		goto out_fail;
  
  	mark_inode_dirty(inode);
  	err = add_nondir(dentry, inode);
  out:
  	return err;
  
  out_fail:
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
108
  	inode_dec_link_count(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
116
117
118
119
120
121
  	iput(inode);
  	goto out;
  }
  
  static int sysv_link(struct dentry * old_dentry, struct inode * dir, 
  	struct dentry * dentry)
  {
  	struct inode *inode = old_dentry->d_inode;
  
  	if (inode->i_nlink >= SYSV_SB(inode->i_sb)->s_link_max)
  		return -EMLINK;
  
  	inode->i_ctime = CURRENT_TIME_SEC;
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
122
  	inode_inc_link_count(inode);
7de9c6ee3   Al Viro   new helper: ihold()
123
  	ihold(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
  
  	return add_nondir(dentry, inode);
  }
18bb1db3e   Al Viro   switch vfs_mkdir(...
127
  static int sysv_mkdir(struct inode * dir, struct dentry *dentry, umode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
  {
  	struct inode * inode;
  	int err = -EMLINK;
  
  	if (dir->i_nlink >= SYSV_SB(dir->i_sb)->s_link_max) 
  		goto out;
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
134
  	inode_inc_link_count(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
  
  	inode = sysv_new_inode(dir, S_IFDIR|mode);
  	err = PTR_ERR(inode);
  	if (IS_ERR(inode))
  		goto out_dir;
  
  	sysv_set_inode(inode, 0);
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
142
  	inode_inc_link_count(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  
  	err = sysv_make_empty(inode, dir);
  	if (err)
  		goto out_fail;
  
  	err = sysv_add_link(dentry, inode);
  	if (err)
  		goto out_fail;
  
          d_instantiate(dentry, inode);
  out:
  	return err;
  
  out_fail:
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
157
158
  	inode_dec_link_count(inode);
  	inode_dec_link_count(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
  	iput(inode);
  out_dir:
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
161
  	inode_dec_link_count(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  	goto out;
  }
  
  static int sysv_unlink(struct inode * dir, struct dentry * dentry)
  {
  	struct inode * inode = dentry->d_inode;
  	struct page * page;
  	struct sysv_dir_entry * de;
  	int err = -ENOENT;
  
  	de = sysv_find_entry(dentry, &page);
  	if (!de)
  		goto out;
  
  	err = sysv_delete_entry (de, page);
  	if (err)
  		goto out;
  
  	inode->i_ctime = dir->i_ctime;
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
181
  	inode_dec_link_count(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
187
188
189
190
191
192
193
194
  out:
  	return err;
  }
  
  static int sysv_rmdir(struct inode * dir, struct dentry * dentry)
  {
  	struct inode *inode = dentry->d_inode;
  	int err = -ENOTEMPTY;
  
  	if (sysv_empty_dir(inode)) {
  		err = sysv_unlink(dir, dentry);
  		if (!err) {
  			inode->i_size = 0;
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
195
196
  			inode_dec_link_count(inode);
  			inode_dec_link_count(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  		}
  	}
  	return err;
  }
  
  /*
   * Anybody can rename anything with this: the permission checks are left to the
   * higher-level routines.
   */
  static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
  		  struct inode * new_dir, struct dentry * new_dentry)
  {
  	struct inode * old_inode = old_dentry->d_inode;
  	struct inode * new_inode = new_dentry->d_inode;
  	struct page * dir_page = NULL;
  	struct sysv_dir_entry * dir_de = NULL;
  	struct page * old_page;
  	struct sysv_dir_entry * old_de;
  	int err = -ENOENT;
  
  	old_de = sysv_find_entry(old_dentry, &old_page);
  	if (!old_de)
  		goto out;
  
  	if (S_ISDIR(old_inode->i_mode)) {
  		err = -EIO;
  		dir_de = sysv_dotdot(old_inode, &dir_page);
  		if (!dir_de)
  			goto out_old;
  	}
  
  	if (new_inode) {
  		struct page * new_page;
  		struct sysv_dir_entry * new_de;
  
  		err = -ENOTEMPTY;
  		if (dir_de && !sysv_empty_dir(new_inode))
  			goto out_dir;
  
  		err = -ENOENT;
  		new_de = sysv_find_entry(new_dentry, &new_page);
  		if (!new_de)
  			goto out_dir;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
  		sysv_set_link(new_de, new_page, old_inode);
  		new_inode->i_ctime = CURRENT_TIME_SEC;
  		if (dir_de)
9a53c3a78   Dave Hansen   [PATCH] r/o bind ...
243
  			drop_nlink(new_inode);
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
244
  		inode_dec_link_count(new_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
  	} else {
  		if (dir_de) {
  			err = -EMLINK;
  			if (new_dir->i_nlink >= SYSV_SB(new_dir->i_sb)->s_link_max)
  				goto out_dir;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  		err = sysv_add_link(new_dentry, old_inode);
4787d45fa   Al Viro   sysv: i_nlink rac...
252
  		if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  			goto out_dir;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  		if (dir_de)
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
255
  			inode_inc_link_count(new_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
  	}
  
  	sysv_delete_entry(old_de, old_page);
4787d45fa   Al Viro   sysv: i_nlink rac...
259
  	mark_inode_dirty(old_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
  
  	if (dir_de) {
  		sysv_set_link(dir_de, dir_page, new_dir);
4e907c3d4   Alexey Dobriyan   [PATCH] sysv: swi...
263
  		inode_dec_link_count(old_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  	}
  	return 0;
  
  out_dir:
  	if (dir_de) {
  		kunmap(dir_page);
  		page_cache_release(dir_page);
  	}
  out_old:
  	kunmap(old_page);
  	page_cache_release(old_page);
  out:
  	return err;
  }
  
  /*
   * directories can handle most operations...
   */
c5ef1c42c   Arjan van de Ven   [PATCH] mark stru...
282
  const struct inode_operations sysv_dir_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
  	.create		= sysv_create,
  	.lookup		= sysv_lookup,
  	.link		= sysv_link,
  	.unlink		= sysv_unlink,
  	.symlink	= sysv_symlink,
  	.mkdir		= sysv_mkdir,
  	.rmdir		= sysv_rmdir,
  	.mknod		= sysv_mknod,
  	.rename		= sysv_rename,
  	.getattr	= sysv_getattr,
  };