aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_vnodeops.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2011-07-08 08:34:23 -0400
committerChristoph Hellwig <hch@lst.de>2011-07-08 08:34:23 -0400
commitc4ed4243c40f97ed5b7b121777bbbc6aeaa722f0 (patch)
treefda6054305f357705687016906a1ca254018c2e7 /fs/xfs/xfs_vnodeops.c
parentdec58f1dfd30a3c3e9dadc808692f4e5cd922745 (diff)
xfs: split xfs_setattr
Split up xfs_setattr into two functions, one for the complex truncate handling, and one for the trivial attribute updates. Also move both new routines to xfs_iops.c as they are fairly Linux-specific. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Alex Elder <aelder@sgi.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
-rw-r--r--fs/xfs/xfs_vnodeops.c426
1 files changed, 1 insertions, 425 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 619720705bc6..a4f56a42ef90 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -50,430 +50,6 @@
50#include "xfs_vnodeops.h" 50#include "xfs_vnodeops.h"
51#include "xfs_trace.h" 51#include "xfs_trace.h"
52 52
53int
54xfs_setattr(
55 struct xfs_inode *ip,
56 struct iattr *iattr,
57 int flags)
58{
59 xfs_mount_t *mp = ip->i_mount;
60 struct inode *inode = VFS_I(ip);
61 int mask = iattr->ia_valid;
62 xfs_trans_t *tp;
63 int code;
64 uint lock_flags;
65 uint commit_flags=0;
66 uid_t uid=0, iuid=0;
67 gid_t gid=0, igid=0;
68 struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2;
69 int need_iolock = 1;
70
71 trace_xfs_setattr(ip);
72
73 if (mp->m_flags & XFS_MOUNT_RDONLY)
74 return XFS_ERROR(EROFS);
75
76 if (XFS_FORCED_SHUTDOWN(mp))
77 return XFS_ERROR(EIO);
78
79 code = -inode_change_ok(inode, iattr);
80 if (code)
81 return code;
82
83 olddquot1 = olddquot2 = NULL;
84 udqp = gdqp = NULL;
85
86 /*
87 * If disk quotas is on, we make sure that the dquots do exist on disk,
88 * before we start any other transactions. Trying to do this later
89 * is messy. We don't care to take a readlock to look at the ids
90 * in inode here, because we can't hold it across the trans_reserve.
91 * If the IDs do change before we take the ilock, we're covered
92 * because the i_*dquot fields will get updated anyway.
93 */
94 if (XFS_IS_QUOTA_ON(mp) && (mask & (ATTR_UID|ATTR_GID))) {
95 uint qflags = 0;
96
97 if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) {
98 uid = iattr->ia_uid;
99 qflags |= XFS_QMOPT_UQUOTA;
100 } else {
101 uid = ip->i_d.di_uid;
102 }
103 if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) {
104 gid = iattr->ia_gid;
105 qflags |= XFS_QMOPT_GQUOTA;
106 } else {
107 gid = ip->i_d.di_gid;
108 }
109
110 /*
111 * We take a reference when we initialize udqp and gdqp,
112 * so it is important that we never blindly double trip on
113 * the same variable. See xfs_create() for an example.
114 */
115 ASSERT(udqp == NULL);
116 ASSERT(gdqp == NULL);
117 code = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip),
118 qflags, &udqp, &gdqp);
119 if (code)
120 return code;
121 }
122
123 /*
124 * For the other attributes, we acquire the inode lock and
125 * first do an error checking pass.
126 */
127 tp = NULL;
128 lock_flags = XFS_ILOCK_EXCL;
129 if (flags & XFS_ATTR_NOLOCK)
130 need_iolock = 0;
131 if (!(mask & ATTR_SIZE)) {
132 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
133 commit_flags = 0;
134 code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp),
135 0, 0, 0);
136 if (code) {
137 lock_flags = 0;
138 goto error_return;
139 }
140 } else {
141 if (need_iolock)
142 lock_flags |= XFS_IOLOCK_EXCL;
143 }
144
145 xfs_ilock(ip, lock_flags);
146
147 /*
148 * Change file ownership. Must be the owner or privileged.
149 */
150 if (mask & (ATTR_UID|ATTR_GID)) {
151 /*
152 * These IDs could have changed since we last looked at them.
153 * But, we're assured that if the ownership did change
154 * while we didn't have the inode locked, inode's dquot(s)
155 * would have changed also.
156 */
157 iuid = ip->i_d.di_uid;
158 igid = ip->i_d.di_gid;
159 gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
160 uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
161
162 /*
163 * Do a quota reservation only if uid/gid is actually
164 * going to change.
165 */
166 if (XFS_IS_QUOTA_RUNNING(mp) &&
167 ((XFS_IS_UQUOTA_ON(mp) && iuid != uid) ||
168 (XFS_IS_GQUOTA_ON(mp) && igid != gid))) {
169 ASSERT(tp);
170 code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
171 capable(CAP_FOWNER) ?
172 XFS_QMOPT_FORCE_RES : 0);
173 if (code) /* out of quota */
174 goto error_return;
175 }
176 }
177
178 /*
179 * Truncate file. Must have write permission and not be a directory.
180 */
181 if (mask & ATTR_SIZE) {
182 /* Short circuit the truncate case for zero length files */
183 if (iattr->ia_size == 0 &&
184 ip->i_size == 0 && ip->i_d.di_nextents == 0) {
185 xfs_iunlock(ip, XFS_ILOCK_EXCL);
186 lock_flags &= ~XFS_ILOCK_EXCL;
187 if (mask & ATTR_CTIME) {
188 inode->i_mtime = inode->i_ctime =
189 current_fs_time(inode->i_sb);
190 xfs_mark_inode_dirty_sync(ip);
191 }
192 code = 0;
193 goto error_return;
194 }
195
196 if (S_ISDIR(ip->i_d.di_mode)) {
197 code = XFS_ERROR(EISDIR);
198 goto error_return;
199 } else if (!S_ISREG(ip->i_d.di_mode)) {
200 code = XFS_ERROR(EINVAL);
201 goto error_return;
202 }
203
204 /*
205 * Make sure that the dquots are attached to the inode.
206 */
207 code = xfs_qm_dqattach_locked(ip, 0);
208 if (code)
209 goto error_return;
210
211 /*
212 * Now we can make the changes. Before we join the inode
213 * to the transaction, if ATTR_SIZE is set then take care of
214 * the part of the truncation that must be done without the
215 * inode lock. This needs to be done before joining the inode
216 * to the transaction, because the inode cannot be unlocked
217 * once it is a part of the transaction.
218 */
219 if (iattr->ia_size > ip->i_size) {
220 /*
221 * Do the first part of growing a file: zero any data
222 * in the last block that is beyond the old EOF. We
223 * need to do this before the inode is joined to the
224 * transaction to modify the i_size.
225 */
226 code = xfs_zero_eof(ip, iattr->ia_size, ip->i_size);
227 if (code)
228 goto error_return;
229 }
230 xfs_iunlock(ip, XFS_ILOCK_EXCL);
231 lock_flags &= ~XFS_ILOCK_EXCL;
232
233 /*
234 * We are going to log the inode size change in this
235 * transaction so any previous writes that are beyond the on
236 * disk EOF and the new EOF that have not been written out need
237 * to be written here. If we do not write the data out, we
238 * expose ourselves to the null files problem.
239 *
240 * Only flush from the on disk size to the smaller of the in
241 * memory file size or the new size as that's the range we
242 * really care about here and prevents waiting for other data
243 * not within the range we care about here.
244 */
245 if (ip->i_size != ip->i_d.di_size &&
246 iattr->ia_size > ip->i_d.di_size) {
247 code = xfs_flush_pages(ip,
248 ip->i_d.di_size, iattr->ia_size,
249 XBF_ASYNC, FI_NONE);
250 if (code)
251 goto error_return;
252 }
253
254 /* wait for all I/O to complete */
255 xfs_ioend_wait(ip);
256
257 code = -block_truncate_page(inode->i_mapping, iattr->ia_size,
258 xfs_get_blocks);
259 if (code)
260 goto error_return;
261
262 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE);
263 code = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
264 XFS_TRANS_PERM_LOG_RES,
265 XFS_ITRUNCATE_LOG_COUNT);
266 if (code)
267 goto error_return;
268
269 truncate_setsize(inode, iattr->ia_size);
270
271 commit_flags = XFS_TRANS_RELEASE_LOG_RES;
272 lock_flags |= XFS_ILOCK_EXCL;
273
274 xfs_ilock(ip, XFS_ILOCK_EXCL);
275
276 xfs_trans_ijoin(tp, ip);
277
278 /*
279 * Only change the c/mtime if we are changing the size
280 * or we are explicitly asked to change it. This handles
281 * the semantic difference between truncate() and ftruncate()
282 * as implemented in the VFS.
283 *
284 * The regular truncate() case without ATTR_CTIME and ATTR_MTIME
285 * is a special case where we need to update the times despite
286 * not having these flags set. For all other operations the
287 * VFS set these flags explicitly if it wants a timestamp
288 * update.
289 */
290 if (iattr->ia_size != ip->i_size &&
291 (!(mask & (ATTR_CTIME | ATTR_MTIME)))) {
292 iattr->ia_ctime = iattr->ia_mtime =
293 current_fs_time(inode->i_sb);
294 mask |= ATTR_CTIME | ATTR_MTIME;
295 }
296
297 if (iattr->ia_size > ip->i_size) {
298 ip->i_d.di_size = iattr->ia_size;
299 ip->i_size = iattr->ia_size;
300 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
301 } else if (iattr->ia_size <= ip->i_size ||
302 (iattr->ia_size == 0 && ip->i_d.di_nextents)) {
303 /*
304 * signal a sync transaction unless
305 * we're truncating an already unlinked
306 * file on a wsync filesystem
307 */
308 code = xfs_itruncate_finish(&tp, ip, iattr->ia_size,
309 XFS_DATA_FORK,
310 ((ip->i_d.di_nlink != 0 ||
311 !(mp->m_flags & XFS_MOUNT_WSYNC))
312 ? 1 : 0));
313 if (code)
314 goto abort_return;
315 /*
316 * Truncated "down", so we're removing references
317 * to old data here - if we now delay flushing for
318 * a long time, we expose ourselves unduly to the
319 * notorious NULL files problem. So, we mark this
320 * vnode and flush it when the file is closed, and
321 * do not wait the usual (long) time for writeout.
322 */
323 xfs_iflags_set(ip, XFS_ITRUNCATED);
324 }
325 } else if (tp) {
326 xfs_trans_ijoin(tp, ip);
327 }
328
329 /*
330 * Change file ownership. Must be the owner or privileged.
331 */
332 if (mask & (ATTR_UID|ATTR_GID)) {
333 /*
334 * CAP_FSETID overrides the following restrictions:
335 *
336 * The set-user-ID and set-group-ID bits of a file will be
337 * cleared upon successful return from chown()
338 */
339 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
340 !capable(CAP_FSETID)) {
341 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
342 }
343
344 /*
345 * Change the ownerships and register quota modifications
346 * in the transaction.
347 */
348 if (iuid != uid) {
349 if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_UQUOTA_ON(mp)) {
350 ASSERT(mask & ATTR_UID);
351 ASSERT(udqp);
352 olddquot1 = xfs_qm_vop_chown(tp, ip,
353 &ip->i_udquot, udqp);
354 }
355 ip->i_d.di_uid = uid;
356 inode->i_uid = uid;
357 }
358 if (igid != gid) {
359 if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_GQUOTA_ON(mp)) {
360 ASSERT(!XFS_IS_PQUOTA_ON(mp));
361 ASSERT(mask & ATTR_GID);
362 ASSERT(gdqp);
363 olddquot2 = xfs_qm_vop_chown(tp, ip,
364 &ip->i_gdquot, gdqp);
365 }
366 ip->i_d.di_gid = gid;
367 inode->i_gid = gid;
368 }
369 }
370
371 /*
372 * Change file access modes.
373 */
374 if (mask & ATTR_MODE) {
375 umode_t mode = iattr->ia_mode;
376
377 if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
378 mode &= ~S_ISGID;
379
380 ip->i_d.di_mode &= S_IFMT;
381 ip->i_d.di_mode |= mode & ~S_IFMT;
382
383 inode->i_mode &= S_IFMT;
384 inode->i_mode |= mode & ~S_IFMT;
385 }
386
387 /*
388 * Change file access or modified times.
389 */
390 if (mask & ATTR_ATIME) {
391 inode->i_atime = iattr->ia_atime;
392 ip->i_d.di_atime.t_sec = iattr->ia_atime.tv_sec;
393 ip->i_d.di_atime.t_nsec = iattr->ia_atime.tv_nsec;
394 ip->i_update_core = 1;
395 }
396 if (mask & ATTR_CTIME) {
397 inode->i_ctime = iattr->ia_ctime;
398 ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec;
399 ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec;
400 ip->i_update_core = 1;
401 }
402 if (mask & ATTR_MTIME) {
403 inode->i_mtime = iattr->ia_mtime;
404 ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec;
405 ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec;
406 ip->i_update_core = 1;
407 }
408
409 /*
410 * And finally, log the inode core if any attribute in it
411 * has been changed.
412 */
413 if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE|
414 ATTR_ATIME|ATTR_CTIME|ATTR_MTIME))
415 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
416
417 XFS_STATS_INC(xs_ig_attrchg);
418
419 /*
420 * If this is a synchronous mount, make sure that the
421 * transaction goes to disk before returning to the user.
422 * This is slightly sub-optimal in that truncates require
423 * two sync transactions instead of one for wsync filesystems.
424 * One for the truncate and one for the timestamps since we
425 * don't want to change the timestamps unless we're sure the
426 * truncate worked. Truncates are less than 1% of the laddis
427 * mix so this probably isn't worth the trouble to optimize.
428 */
429 code = 0;
430 if (mp->m_flags & XFS_MOUNT_WSYNC)
431 xfs_trans_set_sync(tp);
432
433 code = xfs_trans_commit(tp, commit_flags);
434
435 xfs_iunlock(ip, lock_flags);
436
437 /*
438 * Release any dquot(s) the inode had kept before chown.
439 */
440 xfs_qm_dqrele(olddquot1);
441 xfs_qm_dqrele(olddquot2);
442 xfs_qm_dqrele(udqp);
443 xfs_qm_dqrele(gdqp);
444
445 if (code)
446 return code;
447
448 /*
449 * XXX(hch): Updating the ACL entries is not atomic vs the i_mode
450 * update. We could avoid this with linked transactions
451 * and passing down the transaction pointer all the way
452 * to attr_set. No previous user of the generic
453 * Posix ACL code seems to care about this issue either.
454 */
455 if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) {
456 code = -xfs_acl_chmod(inode);
457 if (code)
458 return XFS_ERROR(code);
459 }
460
461 return 0;
462
463 abort_return:
464 commit_flags |= XFS_TRANS_ABORT;
465 error_return:
466 xfs_qm_dqrele(udqp);
467 xfs_qm_dqrele(gdqp);
468 if (tp) {
469 xfs_trans_cancel(tp, commit_flags);
470 }
471 if (lock_flags != 0) {
472 xfs_iunlock(ip, lock_flags);
473 }
474 return code;
475}
476
477/* 53/*
478 * The maximum pathlen is 1024 bytes. Since the minimum file system 54 * The maximum pathlen is 1024 bytes. Since the minimum file system
479 * blocksize is 512 bytes, we can get a max of 2 extents back from 55 * blocksize is 512 bytes, we can get a max of 2 extents back from
@@ -2784,7 +2360,7 @@ xfs_change_file_space(
2784 iattr.ia_valid = ATTR_SIZE; 2360 iattr.ia_valid = ATTR_SIZE;
2785 iattr.ia_size = startoffset; 2361 iattr.ia_size = startoffset;
2786 2362
2787 error = xfs_setattr(ip, &iattr, attr_flags); 2363 error = xfs_setattr_size(ip, &iattr, attr_flags);
2788 2364
2789 if (error) 2365 if (error)
2790 return error; 2366 return error;