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:
parent
1e78879f1e
commit
26f817243b
4 changed files with 33 additions and 7 deletions
|
@ -563,8 +563,6 @@ off_t newsize; /* inode must become this size */
|
||||||
return(EINVAL);
|
return(EINVAL);
|
||||||
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
|
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
|
||||||
return(EFBIG);
|
return(EFBIG);
|
||||||
if (rip->i_size == newsize)
|
|
||||||
return(OK);
|
|
||||||
|
|
||||||
/* Free the actual space if truncating. */
|
/* Free the actual space if truncating. */
|
||||||
if (newsize < rip->i_size) {
|
if (newsize < rip->i_size) {
|
||||||
|
|
|
@ -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 */
|
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)
|
if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL)
|
||||||
return(EINVAL);
|
return(EINVAL);
|
||||||
if (rip->i_size == newsize)
|
|
||||||
return(OK);
|
|
||||||
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
|
if (newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
|
||||||
return(EFBIG);
|
return(EFBIG);
|
||||||
|
|
||||||
|
|
|
@ -310,8 +310,16 @@ int do_truncate()
|
||||||
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
||||||
|
|
||||||
/* Ask FS to truncate the file */
|
/* Ask FS to truncate the file */
|
||||||
if ((r = forbidden(fp, vp, W_BIT)) == OK)
|
if ((r = forbidden(fp, vp, W_BIT)) == OK) {
|
||||||
r = truncate_vnode(vp, 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.
|
||||||
|
*/
|
||||||
|
if (S_ISREG(vp->v_mode) && vp->v_size == length)
|
||||||
|
r = OK;
|
||||||
|
else
|
||||||
|
r = truncate_vnode(vp, length);
|
||||||
|
}
|
||||||
|
|
||||||
unlock_vnode(vp);
|
unlock_vnode(vp);
|
||||||
unlock_vmnt(vmp);
|
unlock_vmnt(vmp);
|
||||||
|
@ -326,6 +334,7 @@ int do_ftruncate()
|
||||||
{
|
{
|
||||||
/* As with do_truncate(), truncate_vnode() does the actual work. */
|
/* As with do_truncate(), truncate_vnode() does the actual work. */
|
||||||
struct filp *rfilp;
|
struct filp *rfilp;
|
||||||
|
struct vnode *vp;
|
||||||
int r;
|
int r;
|
||||||
off_t length;
|
off_t length;
|
||||||
|
|
||||||
|
@ -338,10 +347,18 @@ int do_ftruncate()
|
||||||
if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
|
if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
|
||||||
return(err_code);
|
return(err_code);
|
||||||
|
|
||||||
|
vp = rfilp->filp_vno;
|
||||||
|
|
||||||
if (!(rfilp->filp_mode & W_BIT))
|
if (!(rfilp->filp_mode & W_BIT))
|
||||||
r = EBADF;
|
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
|
else
|
||||||
r = truncate_vnode(rfilp->filp_vno, length);
|
r = truncate_vnode(vp, length);
|
||||||
|
|
||||||
unlock_filp(rfilp);
|
unlock_filp(rfilp);
|
||||||
return(r);
|
return(r);
|
||||||
|
@ -361,6 +378,11 @@ off_t newsize;
|
||||||
assert(tll_locked_by_me(&vp->v_lock));
|
assert(tll_locked_by_me(&vp->v_lock));
|
||||||
file_type = vp->v_mode & I_TYPE;
|
file_type = vp->v_mode & I_TYPE;
|
||||||
if (file_type != I_REGULAR && file_type != I_NAMED_PIPE) return(EINVAL);
|
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)
|
if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
|
||||||
vp->v_size = newsize;
|
vp->v_size = newsize;
|
||||||
return(r);
|
return(r);
|
||||||
|
|
|
@ -126,6 +126,14 @@ void test16a()
|
||||||
if (yc != pc) e(39);
|
if (yc != pc) e(39);
|
||||||
if (ym != pm) e(40);
|
if (ym != pm) e(40);
|
||||||
if (close(fd) != 0) e(41);
|
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. */
|
/* Test the times for link/unlink. */
|
||||||
get_times("T16.e", &a, &c, &m);
|
get_times("T16.e", &a, &c, &m);
|
||||||
|
|
Loading…
Reference in a new issue