VFS: reimplement truncate mtime/ctime fix

POSIX mandates that a file's modification and change time be left
untouched upon truncate/ftruncate iff the file size does not change.
However, an open(O_TRUNC) call must always update the modification and
change time of the file, even if it was already zero-sized. VFS uses
the file systems' truncate call to implement O_TRUNC. This patch
replaces git-255ae85, which did not take into account the open case.
The size check is now moved into VFS, so that individual file systems
need not check for this case anymore.
This commit is contained in:
David van Moolenbroek 2012-04-20 11:12:04 +02:00
parent 1e78879f1e
commit 26f817243b
4 changed files with 33 additions and 7 deletions

View file

@ -563,8 +563,6 @@ off_t newsize; /* inode must become this size */
return(EINVAL);
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
return(EFBIG);
if (rip->i_size == newsize)
return(OK);
/* Free the actual space if truncating. */
if (newsize < rip->i_size) {

View file

@ -533,8 +533,6 @@ off_t newsize; /* inode must become this size */
file_type = rip->i_mode & I_TYPE; /* check to see if file is special */
if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL)
return(EINVAL);
if (rip->i_size == newsize)
return(OK);
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
return(EFBIG);

View file

@ -310,8 +310,16 @@ int do_truncate()
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
/* Ask FS to truncate the file */
if ((r = forbidden(fp, vp, W_BIT)) == OK)
r = truncate_vnode(vp, length);
if ((r = forbidden(fp, vp, W_BIT)) == OK) {
/* If the file size does not change, do not make the actual call. This
* ensures that the file times are retained when the file size remains
* the same, which is a POSIX requirement.
*/
if (S_ISREG(vp->v_mode) && vp->v_size == length)
r = OK;
else
r = truncate_vnode(vp, length);
}
unlock_vnode(vp);
unlock_vmnt(vmp);
@ -326,6 +334,7 @@ int do_ftruncate()
{
/* As with do_truncate(), truncate_vnode() does the actual work. */
struct filp *rfilp;
struct vnode *vp;
int r;
off_t length;
@ -338,10 +347,18 @@ int do_ftruncate()
if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
return(err_code);
vp = rfilp->filp_vno;
if (!(rfilp->filp_mode & W_BIT))
r = EBADF;
else if (S_ISREG(vp->v_mode) && vp->v_size == length)
/* If the file size does not change, do not make the actual call. This
* ensures that the file times are retained when the file size remains
* the same, which is a POSIX requirement.
*/
r = OK;
else
r = truncate_vnode(rfilp->filp_vno, length);
r = truncate_vnode(vp, length);
unlock_filp(rfilp);
return(r);
@ -361,6 +378,11 @@ off_t newsize;
assert(tll_locked_by_me(&vp->v_lock));
file_type = vp->v_mode & I_TYPE;
if (file_type != I_REGULAR && file_type != I_NAMED_PIPE) return(EINVAL);
/* We must not compare the old and the new size here: this function may be
* called for open(2), which requires an update to the file times if O_TRUNC
* is given, even if the file size remains the same.
*/
if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
vp->v_size = newsize;
return(r);

View file

@ -126,6 +126,14 @@ void test16a()
if (yc != pc) e(39);
if (ym != pm) e(40);
if (close(fd) != 0) e(41);
/* Try once more, now without changing the file size. */
sleep(1);
if ( (fd = open("T16.e", O_WRONLY|O_TRUNC)) < 0) e(89);
get_times("T16.e", &a, &c, &m);
if (c != m) e(90);
if (c == xc) e(91);
if (m == xm) e(92);
if (close(fd) != 0) e(93);
/* Test the times for link/unlink. */
get_times("T16.e", &a, &c, &m);