Blame view

fs/attr.c 6.04 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
  /*
   *  linux/fs/attr.c
   *
   *  Copyright (C) 1991, 1992  Linus Torvalds
   *  changes by Thomas Schoebel-Theuer
   */
  
  #include <linux/module.h>
  #include <linux/time.h>
  #include <linux/mm.h>
  #include <linux/string.h>
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
12
  #include <linux/capability.h>
0eeca2830   Robert Love   [PATCH] inotify
13
  #include <linux/fsnotify.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
  #include <linux/fcntl.h>
  #include <linux/quotaops.h>
  #include <linux/security.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
  
  /* Taken over from the old code... */
  
  /* POSIX UID/GID verification for setting inode attributes. */
25d9e2d15   npiggin@suse.de   truncate: new hel...
21
  int inode_change_ok(const struct inode *inode, struct iattr *attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
  {
  	int retval = -EPERM;
  	unsigned int ia_valid = attr->ia_valid;
  
  	/* If force is set do it anyway. */
  	if (ia_valid & ATTR_FORCE)
  		goto fine;
  
  	/* Make sure a caller can chown. */
  	if ((ia_valid & ATTR_UID) &&
da9592ede   David Howells   CRED: Wrap task c...
32
  	    (current_fsuid() != inode->i_uid ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
  	     attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
  		goto error;
  
  	/* Make sure caller can chgrp. */
  	if ((ia_valid & ATTR_GID) &&
da9592ede   David Howells   CRED: Wrap task c...
38
  	    (current_fsuid() != inode->i_uid ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
  	    (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
  	    !capable(CAP_CHOWN))
  		goto error;
  
  	/* Make sure a caller can chmod. */
  	if (ia_valid & ATTR_MODE) {
3bd858ab1   Satyam Sharma   Introduce is_owne...
45
  		if (!is_owner_or_cap(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
  			goto error;
  		/* Also check the setgid bit! */
  		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
  				inode->i_gid) && !capable(CAP_FSETID))
  			attr->ia_mode &= ~S_ISGID;
  	}
  
  	/* Check for setting the inode time. */
9767d7495   Miklos Szeredi   [patch 1/4] vfs: ...
54
  	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
3bd858ab1   Satyam Sharma   Introduce is_owne...
55
  		if (!is_owner_or_cap(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
  			goto error;
  	}
  fine:
  	retval = 0;
  error:
  	return retval;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  EXPORT_SYMBOL(inode_change_ok);
25d9e2d15   npiggin@suse.de   truncate: new hel...
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  /**
   * inode_newsize_ok - may this inode be truncated to a given size
   * @inode:	the inode to be truncated
   * @offset:	the new size to assign to the inode
   * @Returns:	0 on success, -ve errno on failure
   *
   * inode_newsize_ok will check filesystem limits and ulimits to check that the
   * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ
   * when necessary. Caller must not proceed with inode size change if failure is
   * returned. @inode must be a file (not directory), with appropriate
   * permissions to allow truncate (inode_newsize_ok does NOT check these
   * conditions).
   *
   * inode_newsize_ok must be called with i_mutex held.
   */
  int inode_newsize_ok(const struct inode *inode, loff_t offset)
  {
  	if (inode->i_size < offset) {
  		unsigned long limit;
  
  		limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
  		if (limit != RLIM_INFINITY && offset > limit)
  			goto out_sig;
  		if (offset > inode->i_sb->s_maxbytes)
  			goto out_big;
  	} else {
  		/*
  		 * truncation of in-use swapfiles is disallowed - it would
  		 * cause subsequent swapout to scribble on the now-freed
  		 * blocks.
  		 */
  		if (IS_SWAPFILE(inode))
  			return -ETXTBSY;
  	}
  
  	return 0;
  out_sig:
  	send_sig(SIGXFSZ, current, 0);
  out_big:
  	return -EFBIG;
  }
  EXPORT_SYMBOL(inode_newsize_ok);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
  int inode_setattr(struct inode * inode, struct iattr * attr)
  {
  	unsigned int ia_valid = attr->ia_valid;
4a30131e7   NeilBrown   [PATCH] Fix some ...
109
110
111
112
113
114
  
  	if (ia_valid & ATTR_SIZE &&
  	    attr->ia_size != i_size_read(inode)) {
  		int error = vmtruncate(inode, attr->ia_size);
  		if (error)
  			return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  	}
  
  	if (ia_valid & ATTR_UID)
  		inode->i_uid = attr->ia_uid;
  	if (ia_valid & ATTR_GID)
  		inode->i_gid = attr->ia_gid;
  	if (ia_valid & ATTR_ATIME)
  		inode->i_atime = timespec_trunc(attr->ia_atime,
  						inode->i_sb->s_time_gran);
  	if (ia_valid & ATTR_MTIME)
  		inode->i_mtime = timespec_trunc(attr->ia_mtime,
  						inode->i_sb->s_time_gran);
  	if (ia_valid & ATTR_CTIME)
  		inode->i_ctime = timespec_trunc(attr->ia_ctime,
  						inode->i_sb->s_time_gran);
  	if (ia_valid & ATTR_MODE) {
  		umode_t mode = attr->ia_mode;
  
  		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
  			mode &= ~S_ISGID;
  		inode->i_mode = mode;
  	}
  	mark_inode_dirty(inode);
4a30131e7   NeilBrown   [PATCH] Fix some ...
138
139
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  EXPORT_SYMBOL(inode_setattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
  int notify_change(struct dentry * dentry, struct iattr * attr)
  {
  	struct inode *inode = dentry->d_inode;
6de0ec00b   Jeff Layton   VFS: make notify_...
145
  	mode_t mode = inode->i_mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
  	int error;
  	struct timespec now;
  	unsigned int ia_valid = attr->ia_valid;
beb29e058   Miklos Szeredi   [patch 4/4] vfs: ...
149
150
151
152
  	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
  		if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
  			return -EPERM;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
  	now = current_fs_time(inode->i_sb);
  
  	attr->ia_ctime = now;
  	if (!(ia_valid & ATTR_ATIME_SET))
  		attr->ia_atime = now;
  	if (!(ia_valid & ATTR_MTIME_SET))
  		attr->ia_mtime = now;
b53767719   Serge E. Hallyn   Implement file po...
160
161
162
163
164
165
166
167
168
  	if (ia_valid & ATTR_KILL_PRIV) {
  		attr->ia_valid &= ~ATTR_KILL_PRIV;
  		ia_valid &= ~ATTR_KILL_PRIV;
  		error = security_inode_need_killpriv(dentry);
  		if (error > 0)
  			error = security_inode_killpriv(dentry);
  		if (error)
  			return error;
  	}
6de0ec00b   Jeff Layton   VFS: make notify_...
169
170
171
172
173
174
175
176
177
178
179
  
  	/*
  	 * We now pass ATTR_KILL_S*ID to the lower level setattr function so
  	 * that the function has the ability to reinterpret a mode change
  	 * that's due to these bits. This adds an implicit restriction that
  	 * no function will ever call notify_change with both ATTR_MODE and
  	 * ATTR_KILL_S*ID set.
  	 */
  	if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
  	    (ia_valid & ATTR_MODE))
  		BUG();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  	if (ia_valid & ATTR_KILL_SUID) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  		if (mode & S_ISUID) {
6de0ec00b   Jeff Layton   VFS: make notify_...
182
183
  			ia_valid = attr->ia_valid |= ATTR_MODE;
  			attr->ia_mode = (inode->i_mode & ~S_ISUID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
  		}
  	}
  	if (ia_valid & ATTR_KILL_SGID) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
  		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
  			if (!(ia_valid & ATTR_MODE)) {
  				ia_valid = attr->ia_valid |= ATTR_MODE;
  				attr->ia_mode = inode->i_mode;
  			}
  			attr->ia_mode &= ~S_ISGID;
  		}
  	}
6de0ec00b   Jeff Layton   VFS: make notify_...
195
  	if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  		return 0;
a77b72da2   Miklos Szeredi   [patch] vfs: make...
197
198
199
  	error = security_inode_setattr(dentry, attr);
  	if (error)
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
  	if (ia_valid & ATTR_SIZE)
  		down_write(&dentry->d_inode->i_alloc_sem);
  
  	if (inode->i_op && inode->i_op->setattr) {
a77b72da2   Miklos Szeredi   [patch] vfs: make...
204
  		error = inode->i_op->setattr(dentry, attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
  	} else {
  		error = inode_change_ok(inode, attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
  		if (!error) {
  			if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
  			    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
9e3509e27   Jan Kara   vfs: Use lowercas...
210
211
  				error = vfs_dq_transfer(inode, attr) ?
  					-EDQUOT : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
215
216
217
218
  			if (!error)
  				error = inode_setattr(inode, attr);
  		}
  	}
  
  	if (ia_valid & ATTR_SIZE)
  		up_write(&dentry->d_inode->i_alloc_sem);
0eeca2830   Robert Love   [PATCH] inotify
219
220
  	if (!error)
  		fsnotify_change(dentry, ia_valid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
  	return error;
  }
  
  EXPORT_SYMBOL(notify_change);