diff --git a/servers/ext2/link.c b/servers/ext2/link.c index 5786076c4..9f741aa1d 100644 --- a/servers/ext2/link.c +++ b/servers/ext2/link.c @@ -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) { diff --git a/servers/mfs/link.c b/servers/mfs/link.c index 2c499810b..47f6325a4 100644 --- a/servers/mfs/link.c +++ b/servers/mfs/link.c @@ -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); diff --git a/servers/vfs/link.c b/servers/vfs/link.c index e6bfeb253..c343bce4c 100644 --- a/servers/vfs/link.c +++ b/servers/vfs/link.c @@ -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); diff --git a/test/test16.c b/test/test16.c index 74c420508..2f174479f 100644 --- a/test/test16.c +++ b/test/test16.c @@ -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);