Blame view

fs/open.c 26 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /*
   *  linux/fs/open.c
   *
   *  Copyright (C) 1991, 1992  Linus Torvalds
   */
  
  #include <linux/string.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/quotaops.h>
0eeca2830   Robert Love   [PATCH] inotify
11
  #include <linux/fsnotify.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/tty.h>
  #include <linux/namei.h>
  #include <linux/backing-dev.h>
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
17
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
  #include <linux/security.h>
  #include <linux/mount.h>
  #include <linux/vfs.h>
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
21
  #include <linux/fcntl.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  #include <asm/uaccess.h>
  #include <linux/fs.h>
ef3daeda7   Yoav Zach   [PATCH] Don't for...
24
  #include <linux/personality.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  #include <linux/pagemap.h>
  #include <linux/syscalls.h>
ab2af1f50   Dipankar Sarma   [PATCH] files: fi...
27
  #include <linux/rcupdate.h>
73241ccca   Amy Griffis   [PATCH] Collect m...
28
  #include <linux/audit.h>
97ac73506   Amit Arora   sys_fallocate() i...
29
  #include <linux/falloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

726c33422   David Howells   [PATCH] VFS: Perm...
31
  int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
  {
  	int retval = -ENODEV;
726c33422   David Howells   [PATCH] VFS: Perm...
34
  	if (dentry) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  		retval = -ENOSYS;
726c33422   David Howells   [PATCH] VFS: Perm...
36
  		if (dentry->d_sb->s_op->statfs) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  			memset(buf, 0, sizeof(*buf));
726c33422   David Howells   [PATCH] VFS: Perm...
38
  			retval = security_sb_statfs(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  			if (retval)
  				return retval;
726c33422   David Howells   [PATCH] VFS: Perm...
41
  			retval = dentry->d_sb->s_op->statfs(dentry, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
  			if (retval == 0 && buf->f_frsize == 0)
  				buf->f_frsize = buf->f_bsize;
  		}
  	}
  	return retval;
  }
  
  EXPORT_SYMBOL(vfs_statfs);
726c33422   David Howells   [PATCH] VFS: Perm...
50
  static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
  {
  	struct kstatfs st;
  	int retval;
726c33422   David Howells   [PATCH] VFS: Perm...
54
  	retval = vfs_statfs(dentry, &st);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  	if (retval)
  		return retval;
  
  	if (sizeof(*buf) == sizeof(st))
  		memcpy(buf, &st, sizeof(st));
  	else {
  		if (sizeof buf->f_blocks == 4) {
  			if ((st.f_blocks | st.f_bfree | st.f_bavail) &
  			    0xffffffff00000000ULL)
  				return -EOVERFLOW;
  			/*
  			 * f_files and f_ffree may be -1; it's okay to stuff
  			 * that into 32 bits
  			 */
  			if (st.f_files != -1 &&
  			    (st.f_files & 0xffffffff00000000ULL))
  				return -EOVERFLOW;
  			if (st.f_ffree != -1 &&
  			    (st.f_ffree & 0xffffffff00000000ULL))
  				return -EOVERFLOW;
  		}
  
  		buf->f_type = st.f_type;
  		buf->f_bsize = st.f_bsize;
  		buf->f_blocks = st.f_blocks;
  		buf->f_bfree = st.f_bfree;
  		buf->f_bavail = st.f_bavail;
  		buf->f_files = st.f_files;
  		buf->f_ffree = st.f_ffree;
  		buf->f_fsid = st.f_fsid;
  		buf->f_namelen = st.f_namelen;
  		buf->f_frsize = st.f_frsize;
  		memset(buf->f_spare, 0, sizeof(buf->f_spare));
  	}
  	return 0;
  }
726c33422   David Howells   [PATCH] VFS: Perm...
91
  static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
  {
  	struct kstatfs st;
  	int retval;
726c33422   David Howells   [PATCH] VFS: Perm...
95
  	retval = vfs_statfs(dentry, &st);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  	if (retval)
  		return retval;
  
  	if (sizeof(*buf) == sizeof(st))
  		memcpy(buf, &st, sizeof(st));
  	else {
  		buf->f_type = st.f_type;
  		buf->f_bsize = st.f_bsize;
  		buf->f_blocks = st.f_blocks;
  		buf->f_bfree = st.f_bfree;
  		buf->f_bavail = st.f_bavail;
  		buf->f_files = st.f_files;
  		buf->f_ffree = st.f_ffree;
  		buf->f_fsid = st.f_fsid;
  		buf->f_namelen = st.f_namelen;
  		buf->f_frsize = st.f_frsize;
  		memset(buf->f_spare, 0, sizeof(buf->f_spare));
  	}
  	return 0;
  }
  
  asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
  {
  	struct nameidata nd;
  	int error;
  
  	error = user_path_walk(path, &nd);
  	if (!error) {
  		struct statfs tmp;
726c33422   David Howells   [PATCH] VFS: Perm...
125
  		error = vfs_statfs_native(nd.dentry, &tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
  			error = -EFAULT;
  		path_release(&nd);
  	}
  	return error;
  }
  
  
  asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf)
  {
  	struct nameidata nd;
  	long error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
  	error = user_path_walk(path, &nd);
  	if (!error) {
  		struct statfs64 tmp;
726c33422   David Howells   [PATCH] VFS: Perm...
144
  		error = vfs_statfs64(nd.dentry, &tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
  			error = -EFAULT;
  		path_release(&nd);
  	}
  	return error;
  }
  
  
  asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
  {
  	struct file * file;
  	struct statfs tmp;
  	int error;
  
  	error = -EBADF;
  	file = fget(fd);
  	if (!file)
  		goto out;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
163
  	error = vfs_statfs_native(file->f_path.dentry, &tmp);
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
182
183
  	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
  		error = -EFAULT;
  	fput(file);
  out:
  	return error;
  }
  
  asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf)
  {
  	struct file * file;
  	struct statfs64 tmp;
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
  
  	error = -EBADF;
  	file = fget(fd);
  	if (!file)
  		goto out;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
184
  	error = vfs_statfs64(file->f_path.dentry, &tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
  	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
  		error = -EFAULT;
  	fput(file);
  out:
  	return error;
  }
4a30131e7   NeilBrown   [PATCH] Fix some ...
191
192
  int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
  	struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
199
200
201
  {
  	int err;
  	struct iattr newattrs;
  
  	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
  	if (length < 0)
  		return -EINVAL;
  
  	newattrs.ia_size = length;
4a30131e7   NeilBrown   [PATCH] Fix some ...
202
  	newattrs.ia_valid = ATTR_SIZE | time_attrs;
cc4e69dee   Miklos Szeredi   [PATCH] VFS: pass...
203
204
205
206
  	if (filp) {
  		newattrs.ia_file = filp;
  		newattrs.ia_valid |= ATTR_FILE;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207

7b82dc0e6   Linus Torvalds   Remove suid/sgid ...
208
209
  	/* Remove suid/sgid on truncate too */
  	newattrs.ia_valid |= should_remove_suid(dentry);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
210
  	mutex_lock(&dentry->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  	err = notify_change(dentry, &newattrs);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
212
  	mutex_unlock(&dentry->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
  	return err;
  }
b01ec0ef6   Matt Mackall   [PATCH] tiny: Uni...
215
  static long do_sys_truncate(const char __user * path, loff_t length)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  {
  	struct nameidata nd;
  	struct inode * inode;
  	int error;
  
  	error = -EINVAL;
  	if (length < 0)	/* sorry, but loff_t says... */
  		goto out;
  
  	error = user_path_walk(path, &nd);
  	if (error)
  		goto out;
  	inode = nd.dentry->d_inode;
  
  	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
  	error = -EISDIR;
  	if (S_ISDIR(inode->i_mode))
  		goto dput_and_out;
  
  	error = -EINVAL;
  	if (!S_ISREG(inode->i_mode))
  		goto dput_and_out;
e4543eddf   Christoph Hellwig   [PATCH] add a vfs...
238
  	error = vfs_permission(&nd, MAY_WRITE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  	if (error)
  		goto dput_and_out;
  
  	error = -EROFS;
  	if (IS_RDONLY(inode))
  		goto dput_and_out;
  
  	error = -EPERM;
  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
  		goto dput_and_out;
  
  	/*
  	 * Make sure that there are no leases.
  	 */
  	error = break_lease(inode, FMODE_WRITE);
  	if (error)
  		goto dput_and_out;
  
  	error = get_write_access(inode);
  	if (error)
  		goto dput_and_out;
  
  	error = locks_verify_truncate(inode, NULL, length);
  	if (!error) {
  		DQUOT_INIT(inode);
4a30131e7   NeilBrown   [PATCH] Fix some ...
264
  		error = do_truncate(nd.dentry, length, 0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  	}
  	put_write_access(inode);
  
  dput_and_out:
  	path_release(&nd);
  out:
  	return error;
  }
  
  asmlinkage long sys_truncate(const char __user * path, unsigned long length)
  {
  	/* on 32-bit boxen it will cut the range 2^31--2^32-1 off */
  	return do_sys_truncate(path, (long)length);
  }
b01ec0ef6   Matt Mackall   [PATCH] tiny: Uni...
279
  static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  {
  	struct inode * inode;
  	struct dentry *dentry;
  	struct file * file;
  	int error;
  
  	error = -EINVAL;
  	if (length < 0)
  		goto out;
  	error = -EBADF;
  	file = fget(fd);
  	if (!file)
  		goto out;
  
  	/* explicitly opened as large or we are on 64-bit box */
  	if (file->f_flags & O_LARGEFILE)
  		small = 0;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
297
  	dentry = file->f_path.dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  	inode = dentry->d_inode;
  	error = -EINVAL;
  	if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
  		goto out_putf;
  
  	error = -EINVAL;
  	/* Cannot ftruncate over 2^31 bytes without large file support */
  	if (small && length > MAX_NON_LFS)
  		goto out_putf;
  
  	error = -EPERM;
  	if (IS_APPEND(inode))
  		goto out_putf;
  
  	error = locks_verify_truncate(inode, file, length);
  	if (!error)
6e656be89   Peter Staubach   [PATCH] ftruncate...
314
  		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
  out_putf:
  	fput(file);
  out:
  	return error;
  }
  
  asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length)
  {
0a489cb3b   Linus Torvalds   x86: don't allow ...
323
  	long ret = do_sys_ftruncate(fd, length, 1);
385910f2b   Linus Torvalds   x86: be careful a...
324
  	/* avoid REGPARM breakage on x86: */
0a489cb3b   Linus Torvalds   x86: don't allow ...
325
326
  	prevent_tail_call(ret);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
334
335
336
337
  }
  
  /* LFS versions of truncate are only needed on 32 bit machines */
  #if BITS_PER_LONG == 32
  asmlinkage long sys_truncate64(const char __user * path, loff_t length)
  {
  	return do_sys_truncate(path, length);
  }
  
  asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
  {
0a489cb3b   Linus Torvalds   x86: don't allow ...
338
  	long ret = do_sys_ftruncate(fd, length, 0);
385910f2b   Linus Torvalds   x86: be careful a...
339
  	/* avoid REGPARM breakage on x86: */
0a489cb3b   Linus Torvalds   x86: don't allow ...
340
341
  	prevent_tail_call(ret);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
  }
  #endif
97ac73506   Amit Arora   sys_fallocate() i...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len)
  {
  	struct file *file;
  	struct inode *inode;
  	long ret = -EINVAL;
  
  	if (offset < 0 || len <= 0)
  		goto out;
  
  	/* Return error if mode is not supported */
  	ret = -EOPNOTSUPP;
  	if (mode && !(mode & FALLOC_FL_KEEP_SIZE))
  		goto out;
  
  	ret = -EBADF;
  	file = fget(fd);
  	if (!file)
  		goto out;
  	if (!(file->f_mode & FMODE_WRITE))
  		goto out_fput;
  	/*
  	 * Revalidate the write permissions, in case security policy has
  	 * changed since the files were opened.
  	 */
  	ret = security_file_permission(file, MAY_WRITE);
  	if (ret)
  		goto out_fput;
  
  	inode = file->f_path.dentry->d_inode;
  
  	ret = -ESPIPE;
  	if (S_ISFIFO(inode->i_mode))
  		goto out_fput;
  
  	ret = -ENODEV;
  	/*
  	 * Let individual file system decide if it supports preallocation
  	 * for directories or not.
  	 */
  	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
  		goto out_fput;
  
  	ret = -EFBIG;
  	/* Check for wrap through zero too */
  	if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
  		goto out_fput;
  
  	if (inode->i_op && inode->i_op->fallocate)
  		ret = inode->i_op->fallocate(inode, mode, offset, len);
  	else
  		ret = -ENOSYS;
  
  out_fput:
  	fput(file);
  out:
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
405
  /*
   * access() needs to use the real uid/gid, not the effective uid/gid.
   * We do this by temporarily clearing all FS-related capabilities and
   * switching the fsuid/fsgid around to the real ones.
   */
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
406
  asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  {
  	struct nameidata nd;
  	int old_fsuid, old_fsgid;
  	kernel_cap_t old_cap;
  	int res;
  
  	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
  		return -EINVAL;
  
  	old_fsuid = current->fsuid;
  	old_fsgid = current->fsgid;
  	old_cap = current->cap_effective;
  
  	current->fsuid = current->uid;
  	current->fsgid = current->gid;
  
  	/*
  	 * Clear the capabilities if we switch to a non-root user
  	 *
  	 * FIXME: There is a race here against sys_capset.  The
  	 * capabilities can change yet we will restore the old
  	 * value below.  We should hold task_capabilities_lock,
  	 * but we cannot because user_path_walk can sleep.
  	 */
  	if (current->uid)
  		cap_clear(current->cap_effective);
  	else
  		current->cap_effective = current->cap_permitted;
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
435
  	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
6902d925d   Dave Hansen   [PATCH] r/o bind ...
436
437
438
439
440
441
442
443
444
445
446
  	if (res)
  		goto out;
  
  	res = vfs_permission(&nd, mode);
  	/* SuS v2 requires we report a read only fs too */
  	if(res || !(mode & S_IWOTH) ||
  	   special_file(nd.dentry->d_inode->i_mode))
  		goto out_path_release;
  
  	if(IS_RDONLY(nd.dentry->d_inode))
  		res = -EROFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447

6902d925d   Dave Hansen   [PATCH] r/o bind ...
448
449
450
  out_path_release:
  	path_release(&nd);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
  	current->fsuid = old_fsuid;
  	current->fsgid = old_fsgid;
  	current->cap_effective = old_cap;
  
  	return res;
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
457
458
459
460
  asmlinkage long sys_access(const char __user *filename, int mode)
  {
  	return sys_faccessat(AT_FDCWD, filename, mode);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
463
464
  asmlinkage long sys_chdir(const char __user * filename)
  {
  	struct nameidata nd;
  	int error;
650a89834   Miklos Szeredi   [PATCH] vfs: defi...
465
466
  	error = __user_walk(filename,
  			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
  	if (error)
  		goto out;
e4543eddf   Christoph Hellwig   [PATCH] add a vfs...
469
  	error = vfs_permission(&nd, MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  	if (error)
  		goto dput_and_out;
  
  	set_fs_pwd(current->fs, nd.mnt, nd.dentry);
  
  dput_and_out:
  	path_release(&nd);
  out:
  	return error;
  }
  
  asmlinkage long sys_fchdir(unsigned int fd)
  {
  	struct file *file;
  	struct dentry *dentry;
  	struct inode *inode;
  	struct vfsmount *mnt;
  	int error;
  
  	error = -EBADF;
  	file = fget(fd);
  	if (!file)
  		goto out;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
493
494
  	dentry = file->f_path.dentry;
  	mnt = file->f_path.mnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
  	inode = dentry->d_inode;
  
  	error = -ENOTDIR;
  	if (!S_ISDIR(inode->i_mode))
  		goto out_putf;
8c744fb83   Christoph Hellwig   [PATCH] add a fil...
500
  	error = file_permission(file, MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
  	if (!error)
  		set_fs_pwd(current->fs, mnt, dentry);
  out_putf:
  	fput(file);
  out:
  	return error;
  }
  
  asmlinkage long sys_chroot(const char __user * filename)
  {
  	struct nameidata nd;
  	int error;
  
  	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
  	if (error)
  		goto out;
e4543eddf   Christoph Hellwig   [PATCH] add a vfs...
517
  	error = vfs_permission(&nd, MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  	if (error)
  		goto dput_and_out;
  
  	error = -EPERM;
  	if (!capable(CAP_SYS_CHROOT))
  		goto dput_and_out;
  
  	set_fs_root(current->fs, nd.mnt, nd.dentry);
  	set_fs_altroot();
  	error = 0;
  dput_and_out:
  	path_release(&nd);
  out:
  	return error;
  }
  
  asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
  {
  	struct inode * inode;
  	struct dentry * dentry;
  	struct file * file;
  	int err = -EBADF;
  	struct iattr newattrs;
  
  	file = fget(fd);
  	if (!file)
  		goto out;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
545
  	dentry = file->f_path.dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
  	inode = dentry->d_inode;
9c937dcc7   Amy Griffis   [PATCH] log more ...
547
  	audit_inode(NULL, inode);
73241ccca   Amy Griffis   [PATCH] Collect m...
548

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
552
553
554
  	err = -EROFS;
  	if (IS_RDONLY(inode))
  		goto out_putf;
  	err = -EPERM;
  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
  		goto out_putf;
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
555
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
  	if (mode == (mode_t) -1)
  		mode = inode->i_mode;
  	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
  	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
  	err = notify_change(dentry, &newattrs);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
561
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
566
567
  
  out_putf:
  	fput(file);
  out:
  	return err;
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
568
569
  asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
  			     mode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
573
574
  {
  	struct nameidata nd;
  	struct inode * inode;
  	int error;
  	struct iattr newattrs;
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
575
  	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
585
586
  	if (error)
  		goto out;
  	inode = nd.dentry->d_inode;
  
  	error = -EROFS;
  	if (IS_RDONLY(inode))
  		goto dput_and_out;
  
  	error = -EPERM;
  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
  		goto dput_and_out;
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
587
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
592
  	if (mode == (mode_t) -1)
  		mode = inode->i_mode;
  	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
  	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
  	error = notify_change(nd.dentry, &newattrs);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
593
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
596
597
598
599
  
  dput_and_out:
  	path_release(&nd);
  out:
  	return error;
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
600
601
602
603
  asmlinkage long sys_chmod(const char __user *filename, mode_t mode)
  {
  	return sys_fchmodat(AT_FDCWD, filename, mode);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
  static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
  {
  	struct inode * inode;
  	int error;
  	struct iattr newattrs;
  
  	error = -ENOENT;
  	if (!(inode = dentry->d_inode)) {
  		printk(KERN_ERR "chown_common: NULL inode
  ");
  		goto out;
  	}
  	error = -EROFS;
  	if (IS_RDONLY(inode))
  		goto out;
  	error = -EPERM;
  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
  		goto out;
  	newattrs.ia_valid =  ATTR_CTIME;
  	if (user != (uid_t) -1) {
  		newattrs.ia_valid |= ATTR_UID;
  		newattrs.ia_uid = user;
  	}
  	if (group != (gid_t) -1) {
  		newattrs.ia_valid |= ATTR_GID;
  		newattrs.ia_gid = group;
  	}
  	if (!S_ISDIR(inode->i_mode))
  		newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
633
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  	error = notify_change(dentry, &newattrs);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
635
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
644
645
  out:
  	return error;
  }
  
  asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
  {
  	struct nameidata nd;
  	int error;
  
  	error = user_path_walk(filename, &nd);
6902d925d   Dave Hansen   [PATCH] r/o bind ...
646
647
648
649
650
  	if (error)
  		goto out;
  	error = chown_common(nd.dentry, user, group);
  	path_release(&nd);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
  	return error;
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
653
654
655
656
657
658
659
660
661
662
663
664
  asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
  			     gid_t group, int flag)
  {
  	struct nameidata nd;
  	int error = -EINVAL;
  	int follow;
  
  	if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
  		goto out;
  
  	follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
  	error = __user_walk_fd(dfd, filename, follow, &nd);
6902d925d   Dave Hansen   [PATCH] r/o bind ...
665
666
667
668
  	if (error)
  		goto out;
  	error = chown_common(nd.dentry, user, group);
  	path_release(&nd);
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
669
670
671
  out:
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
674
675
676
677
  asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
  {
  	struct nameidata nd;
  	int error;
  
  	error = user_path_walk_link(filename, &nd);
6902d925d   Dave Hansen   [PATCH] r/o bind ...
678
679
680
681
682
  	if (error)
  		goto out;
  	error = chown_common(nd.dentry, user, group);
  	path_release(&nd);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
685
686
687
688
689
690
  	return error;
  }
  
  
  asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
  {
  	struct file * file;
  	int error = -EBADF;
6902d925d   Dave Hansen   [PATCH] r/o bind ...
691
  	struct dentry * dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
  
  	file = fget(fd);
6902d925d   Dave Hansen   [PATCH] r/o bind ...
694
695
  	if (!file)
  		goto out;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
696
  	dentry = file->f_path.dentry;
6902d925d   Dave Hansen   [PATCH] r/o bind ...
697
698
699
700
  	audit_inode(NULL, dentry->d_inode);
  	error = chown_common(dentry, user, group);
  	fput(file);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
  	return error;
  }
a1a5b3d93   Peter Staubach   [PATCH] open retu...
703
  static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
704
705
  					int flags, struct file *f,
  					int (*open)(struct inode *, struct file *))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
  	struct inode *inode;
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  	f->f_flags = flags;
a1a5b3d93   Peter Staubach   [PATCH] open retu...
710
711
  	f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK |
  				FMODE_PREAD | FMODE_PWRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
715
716
717
718
719
  	inode = dentry->d_inode;
  	if (f->f_mode & FMODE_WRITE) {
  		error = get_write_access(inode);
  		if (error)
  			goto cleanup_file;
  	}
  
  	f->f_mapping = inode->i_mapping;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
720
721
  	f->f_path.dentry = dentry;
  	f->f_path.mnt = mnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
723
724
  	f->f_pos = 0;
  	f->f_op = fops_get(inode->i_fop);
  	file_move(f, &inode->i_sb->s_files);
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
725
726
727
728
  	if (!open && f->f_op)
  		open = f->f_op->open;
  	if (open) {
  		error = open(inode, f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
731
  		if (error)
  			goto cleanup_all;
  	}
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
732

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
738
  	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
  
  	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
  
  	/* NB: we're sure to have correct a_ops only after f_op->open */
  	if (f->f_flags & O_DIRECT) {
ceffc0785   Carsten Otte   [PATCH] xip: fs/m...
739
740
741
  		if (!f->f_mapping->a_ops ||
  		    ((!f->f_mapping->a_ops->direct_IO) &&
  		    (!f->f_mapping->a_ops->get_xip_page))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
748
749
750
751
752
753
  			fput(f);
  			f = ERR_PTR(-EINVAL);
  		}
  	}
  
  	return f;
  
  cleanup_all:
  	fops_put(f->f_op);
  	if (f->f_mode & FMODE_WRITE)
  		put_write_access(inode);
  	file_kill(f);
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
754
755
  	f->f_path.dentry = NULL;
  	f->f_path.mnt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
  cleanup_file:
  	put_filp(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
760
761
  	dput(dentry);
  	mntput(mnt);
  	return ERR_PTR(error);
  }
a1a5b3d93   Peter Staubach   [PATCH] open retu...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
  /*
   * Note that while the flag value (low two bits) for sys_open means:
   *	00 - read-only
   *	01 - write-only
   *	10 - read-write
   *	11 - special
   * it is changed into
   *	00 - no permissions needed
   *	01 - read-permission
   *	10 - write-permission
   *	11 - read-write
   * for the internal routines (ie open_namei()/follow_link() etc). 00 is
   * used by symlinks.
   */
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
776
777
  static struct file *do_filp_open(int dfd, const char *filename, int flags,
  				 int mode)
a1a5b3d93   Peter Staubach   [PATCH] open retu...
778
779
780
  {
  	int namei_flags, error;
  	struct nameidata nd;
a1a5b3d93   Peter Staubach   [PATCH] open retu...
781
782
783
784
  
  	namei_flags = flags;
  	if ((namei_flags+1) & O_ACCMODE)
  		namei_flags++;
a1a5b3d93   Peter Staubach   [PATCH] open retu...
785

5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
786
  	error = open_namei(dfd, filename, namei_flags, mode, &nd);
a1a5b3d93   Peter Staubach   [PATCH] open retu...
787
  	if (!error)
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
788
  		return nameidata_to_filp(&nd, flags);
a1a5b3d93   Peter Staubach   [PATCH] open retu...
789

a1a5b3d93   Peter Staubach   [PATCH] open retu...
790
791
  	return ERR_PTR(error);
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
792
793
794
795
796
  
  struct file *filp_open(const char *filename, int flags, int mode)
  {
  	return do_filp_open(AT_FDCWD, filename, flags, mode);
  }
a1a5b3d93   Peter Staubach   [PATCH] open retu...
797
  EXPORT_SYMBOL(filp_open);
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
798
799
800
801
802
803
804
805
806
807
  /**
   * lookup_instantiate_filp - instantiates the open intent filp
   * @nd: pointer to nameidata
   * @dentry: pointer to dentry
   * @open: open callback
   *
   * Helper for filesystems that want to use lookup open intents and pass back
   * a fully instantiated struct file to the caller.
   * This function is meant to be called from within a filesystem's
   * lookup method.
9a56c2139   Oleg Drokin   [PATCH] Add looku...
808
809
810
811
   * Beware of calling it for non-regular files! Those ->open methods might block
   * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo,
   * leading to a deadlock, as nobody can open that fifo anymore, because
   * another process to open fifo will block on locked parent when doing lookup).
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
   * Note that in case of error, nd->intent.open.file is destroyed, but the
   * path information remains valid.
   * If the open callback is set to NULL, then the standard f_op->open()
   * filesystem callback is substituted.
   */
  struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
  		int (*open)(struct inode *, struct file *))
  {
  	if (IS_ERR(nd->intent.open.file))
  		goto out;
  	if (IS_ERR(dentry))
  		goto out_err;
  	nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt),
  					     nd->intent.open.flags - 1,
  					     nd->intent.open.file,
  					     open);
  out:
  	return nd->intent.open.file;
  out_err:
  	release_open_intent(nd);
  	nd->intent.open.file = (struct file *)dentry;
  	goto out;
  }
  EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
  
  /**
   * nameidata_to_filp - convert a nameidata to an open filp.
   * @nd: pointer to nameidata
   * @flags: open flags
   *
   * Note that this function destroys the original nameidata
   */
  struct file *nameidata_to_filp(struct nameidata *nd, int flags)
  {
  	struct file *filp;
  
  	/* Pick up the filp from the open intent */
  	filp = nd->intent.open.file;
  	/* Has the filesystem initialised the file for us? */
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
851
  	if (filp->f_path.dentry == NULL)
834f2a4a1   Trond Myklebust   VFS: Allow the fi...
852
853
854
855
856
  		filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
  	else
  		path_release(nd);
  	return filp;
  }
6fdcc2162   Peter Staubach   [PATCH] memory le...
857
858
859
860
  /*
   * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
   * error.
   */
a1a5b3d93   Peter Staubach   [PATCH] open retu...
861
862
863
864
865
866
867
  struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
  {
  	int error;
  	struct file *f;
  
  	error = -ENFILE;
  	f = get_empty_filp();
6fdcc2162   Peter Staubach   [PATCH] memory le...
868
869
870
  	if (f == NULL) {
  		dput(dentry);
  		mntput(mnt);
a1a5b3d93   Peter Staubach   [PATCH] open retu...
871
  		return ERR_PTR(error);
6fdcc2162   Peter Staubach   [PATCH] memory le...
872
  	}
a1a5b3d93   Peter Staubach   [PATCH] open retu...
873

834f2a4a1   Trond Myklebust   VFS: Allow the fi...
874
  	return __dentry_open(dentry, mnt, flags, f, NULL);
a1a5b3d93   Peter Staubach   [PATCH] open retu...
875
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
879
880
  EXPORT_SYMBOL(dentry_open);
  
  /*
   * Find an empty file descriptor entry, and mark it busy.
   */
4a19542e5   Ulrich Drepper   O_CLOEXEC for SCM...
881
  int get_unused_fd_flags(int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
884
  {
  	struct files_struct * files = current->files;
  	int fd, error;
badf16621   Dipankar Sarma   [PATCH] files: br...
885
  	struct fdtable *fdt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
887
888
889
890
  
    	error = -EMFILE;
  	spin_lock(&files->file_lock);
  
  repeat:
badf16621   Dipankar Sarma   [PATCH] files: br...
891
  	fdt = files_fdtable(files);
bbea9f696   Vadim Lobanov   [PATCH] fdtable: ...
892
  	fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
0c9e63fd3   Eric Dumazet   [PATCH] Shrinks s...
893
  				files->next_fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
  
  	/*
  	 * N.B. For clone tasks sharing a files structure, this test
  	 * will limit the total number of files that can be opened.
  	 */
  	if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
  		goto out;
  
  	/* Do we need to expand the fd array or fd set?  */
  	error = expand_files(files, fd);
  	if (error < 0)
  		goto out;
  
  	if (error) {
  		/*
  	 	 * If we needed to expand the fs array we
  		 * might have blocked - try again.
  		 */
  		error = -EMFILE;
  		goto repeat;
  	}
badf16621   Dipankar Sarma   [PATCH] files: br...
915
  	FD_SET(fd, fdt->open_fds);
f23513e8d   Ulrich Drepper   Introduce O_CLOEXEC
916
917
918
919
  	if (flags & O_CLOEXEC)
  		FD_SET(fd, fdt->close_on_exec);
  	else
  		FD_CLR(fd, fdt->close_on_exec);
0c9e63fd3   Eric Dumazet   [PATCH] Shrinks s...
920
  	files->next_fd = fd + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
  #if 1
  	/* Sanity check */
badf16621   Dipankar Sarma   [PATCH] files: br...
923
  	if (fdt->fd[fd] != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
  		printk(KERN_WARNING "get_unused_fd: slot %d not NULL!
  ", fd);
badf16621   Dipankar Sarma   [PATCH] files: br...
926
  		fdt->fd[fd] = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
928
929
930
931
932
933
934
  	}
  #endif
  	error = fd;
  
  out:
  	spin_unlock(&files->file_lock);
  	return error;
  }
f23513e8d   Ulrich Drepper   Introduce O_CLOEXEC
935
936
937
938
  int get_unused_fd(void)
  {
  	return get_unused_fd_flags(0);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
  EXPORT_SYMBOL(get_unused_fd);
b01ec0ef6   Matt Mackall   [PATCH] tiny: Uni...
940
  static void __put_unused_fd(struct files_struct *files, unsigned int fd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
941
  {
badf16621   Dipankar Sarma   [PATCH] files: br...
942
943
  	struct fdtable *fdt = files_fdtable(files);
  	__FD_CLR(fd, fdt->open_fds);
0c9e63fd3   Eric Dumazet   [PATCH] Shrinks s...
944
945
  	if (fd < files->next_fd)
  		files->next_fd = fd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
948
949
950
951
952
953
954
955
956
957
958
  }
  
  void fastcall put_unused_fd(unsigned int fd)
  {
  	struct files_struct *files = current->files;
  	spin_lock(&files->file_lock);
  	__put_unused_fd(files, fd);
  	spin_unlock(&files->file_lock);
  }
  
  EXPORT_SYMBOL(put_unused_fd);
  
  /*
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
959
   * Install a file pointer in the fd array.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
964
965
966
967
968
969
970
971
972
973
   *
   * The VFS is full of places where we drop the files lock between
   * setting the open_fds bitmap and installing the file in the file
   * array.  At any such point, we are vulnerable to a dup2() race
   * installing a file in the array before us.  We need to detect this and
   * fput() the struct file we are about to overwrite in this case.
   *
   * It should never happen - if we allow dup2() do it, _really_ bad things
   * will follow.
   */
  
  void fastcall fd_install(unsigned int fd, struct file * file)
  {
  	struct files_struct *files = current->files;
badf16621   Dipankar Sarma   [PATCH] files: br...
974
  	struct fdtable *fdt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
  	spin_lock(&files->file_lock);
badf16621   Dipankar Sarma   [PATCH] files: br...
976
  	fdt = files_fdtable(files);
ab2af1f50   Dipankar Sarma   [PATCH] files: fi...
977
978
  	BUG_ON(fdt->fd[fd] != NULL);
  	rcu_assign_pointer(fdt->fd[fd], file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
981
982
  	spin_unlock(&files->file_lock);
  }
  
  EXPORT_SYMBOL(fd_install);
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
983
  long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  {
e922efc34   Miklos Szeredi   [PATCH] remove du...
985
986
  	char *tmp = getname(filename);
  	int fd = PTR_ERR(tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
  	if (!IS_ERR(tmp)) {
f23513e8d   Ulrich Drepper   Introduce O_CLOEXEC
989
  		fd = get_unused_fd_flags(flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
  		if (fd >= 0) {
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
991
  			struct file *f = do_filp_open(dfd, tmp, flags, mode);
fed2fc18a   Telemaque Ndizihiwe   [PATCH] sys_open(...
992
993
994
995
  			if (IS_ERR(f)) {
  				put_unused_fd(fd);
  				fd = PTR_ERR(f);
  			} else {
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
996
  				fsnotify_open(f->f_path.dentry);
fed2fc18a   Telemaque Ndizihiwe   [PATCH] sys_open(...
997
998
  				fd_install(fd, f);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
1002
  		putname(tmp);
  	}
  	return fd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
  }
e922efc34   Miklos Szeredi   [PATCH] remove du...
1004
1005
1006
  
  asmlinkage long sys_open(const char __user *filename, int flags, int mode)
  {
385910f2b   Linus Torvalds   x86: be careful a...
1007
  	long ret;
e922efc34   Miklos Szeredi   [PATCH] remove du...
1008
1009
  	if (force_o_largefile())
  		flags |= O_LARGEFILE;
385910f2b   Linus Torvalds   x86: be careful a...
1010
1011
1012
1013
  	ret = do_sys_open(AT_FDCWD, filename, flags, mode);
  	/* avoid REGPARM breakage on x86: */
  	prevent_tail_call(ret);
  	return ret;
e922efc34   Miklos Szeredi   [PATCH] remove du...
1014
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1015
  EXPORT_SYMBOL_GPL(sys_open);
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
1016
1017
1018
  asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
  			   int mode)
  {
385910f2b   Linus Torvalds   x86: be careful a...
1019
  	long ret;
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
1020
1021
  	if (force_o_largefile())
  		flags |= O_LARGEFILE;
385910f2b   Linus Torvalds   x86: be careful a...
1022
1023
1024
1025
  	ret = do_sys_open(dfd, filename, flags, mode);
  	/* avoid REGPARM breakage on x86: */
  	prevent_tail_call(ret);
  	return ret;
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
1026
  }
5590ff0d5   Ulrich Drepper   [PATCH] vfs: *at ...
1027

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
  #ifndef __alpha__
  
  /*
   * For backward compatibility?  Maybe this should be moved
   * into arch/i386 instead?
   */
  asmlinkage long sys_creat(const char __user * pathname, int mode)
  {
  	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
  }
  
  #endif
  
  /*
   * "id" is the POSIX thread ID. We use the
   * files pointer for this..
   */
  int filp_close(struct file *filp, fl_owner_t id)
  {
45778ca81   Christoph Lameter   [PATCH] Remove f_...
1047
  	int retval = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
1050
1051
  
  	if (!file_count(filp)) {
  		printk(KERN_ERR "VFS: Close: file count is 0
  ");
45778ca81   Christoph Lameter   [PATCH] Remove f_...
1052
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
  	}
45778ca81   Christoph Lameter   [PATCH] Remove f_...
1054
  	if (filp->f_op && filp->f_op->flush)
75e1fcc0b   Miklos Szeredi   [PATCH] vfs: add ...
1055
  		retval = filp->f_op->flush(filp, id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  
  	dnotify_flush(filp, id);
  	locks_remove_posix(filp, id);
  	fput(filp);
  	return retval;
  }
  
  EXPORT_SYMBOL(filp_close);
  
  /*
   * Careful here! We test whether the file pointer is NULL before
   * releasing the fd. This ensures that one clone task can't release
   * an fd while another clone is opening it.
   */
  asmlinkage long sys_close(unsigned int fd)
  {
  	struct file * filp;
  	struct files_struct *files = current->files;
badf16621   Dipankar Sarma   [PATCH] files: br...
1074
  	struct fdtable *fdt;
ee731f4f7   Ernie Petrides   [PATCH] fix wrong...
1075
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
  
  	spin_lock(&files->file_lock);
badf16621   Dipankar Sarma   [PATCH] files: br...
1078
1079
  	fdt = files_fdtable(files);
  	if (fd >= fdt->max_fds)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
  		goto out_unlock;
badf16621   Dipankar Sarma   [PATCH] files: br...
1081
  	filp = fdt->fd[fd];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
  	if (!filp)
  		goto out_unlock;
ab2af1f50   Dipankar Sarma   [PATCH] files: fi...
1084
  	rcu_assign_pointer(fdt->fd[fd], NULL);
badf16621   Dipankar Sarma   [PATCH] files: br...
1085
  	FD_CLR(fd, fdt->close_on_exec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
  	__put_unused_fd(files, fd);
  	spin_unlock(&files->file_lock);
ee731f4f7   Ernie Petrides   [PATCH] fix wrong...
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
  	retval = filp_close(filp, files);
  
  	/* can't restart close syscall because file table entry was cleared */
  	if (unlikely(retval == -ERESTARTSYS ||
  		     retval == -ERESTARTNOINTR ||
  		     retval == -ERESTARTNOHAND ||
  		     retval == -ERESTART_RESTARTBLOCK))
  		retval = -EINTR;
  
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
  
  out_unlock:
  	spin_unlock(&files->file_lock);
  	return -EBADF;
  }
  
  EXPORT_SYMBOL(sys_close);
  
  /*
   * This routine simulates a hangup on the tty, to arrange that users
   * are given clean terminals at login time.
   */
  asmlinkage long sys_vhangup(void)
  {
  	if (capable(CAP_SYS_TTY_CONFIG)) {
24ec839c4   Peter Zijlstra   [PATCH] tty: ->si...
1113
  		/* XXX: this needs locking */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  		tty_vhangup(current->signal->tty);
  		return 0;
  	}
  	return -EPERM;
  }
  
  /*
   * Called when an inode is about to be open.
   * We use this to disallow opening large files on 32bit systems if
   * the caller didn't specify O_LARGEFILE.  On 64bit systems we force
   * on this flag in sys_open.
   */
  int generic_file_open(struct inode * inode, struct file * filp)
  {
  	if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
  		return -EFBIG;
  	return 0;
  }
  
  EXPORT_SYMBOL(generic_file_open);
  
  /*
   * This is used by subsystems that don't want seekable
   * file descriptors
   */
  int nonseekable_open(struct inode *inode, struct file *filp)
  {
  	filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
  	return 0;
  }
  
  EXPORT_SYMBOL(nonseekable_open);