public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Timothy Shimmin <tes@sgi.com>
To: Roger Willcocks <roger@filmlight.ltd.uk>
Cc: xfs@oss.sgi.com
Subject: Re: bug: truncate to zero + setuid
Date: Fri, 02 Nov 2007 12:11:28 +1100	[thread overview]
Message-ID: <472A7940.5070800@sgi.com> (raw)
In-Reply-To: <472769A1.5090605@filmlight.ltd.uk>

Hi Roger,

Roger Willcocks wrote:
> Timothy Shimmin wrote:
>> I presume it was done where it was done so that the inode was locked 
>> and we
>> were under the XFS_AT_SIZE predicate.
>>
>> I was just thinking of something like...
>> but I'm probably missing something.
>>
>> Index: 2.6.x-xfs/fs/xfs/xfs_vnodeops.c
>> ===================================================================
>> --- 2.6.x-xfs.orig/fs/xfs/xfs_vnodeops.c        2007-10-12 
>> 16:06:15.000000000 +1000
>> +++ 2.6.x-xfs/fs/xfs/xfs_vnodeops.c     2007-10-30 14:59:46.418837757 
>> +1100
>> @@ -304,6 +304,24 @@
>>         }
>>
>>         /*
>> +        * Short circuit the truncate case for zero length files.
>> +        * If more mask bits are set, then just remove the SIZE one
>> +        * and keep going.
>> +        */
>> +       if (mask & XFS_AT_SIZE) {
>> +               xfs_ilock(ip, XFS_ILOCK_SHARED);
>> +               if ((vap->va_size == 0) && (ip->i_size == 0) && 
>> (ip->i_d.di_nextents == 0)) {
>> +                       if (mask & ~XFS_AT_SIZE) {
>> +                               mask &= ~XFS_AT_SIZE;
>> +                       } else {
>> +                               xfs_iunlock(ip, XFS_ILOCK_SHARED);
>> +                               return 0;
>> +                       }
>> +               }
>> +               xfs_iunlock(ip, XFS_ILOCK_SHARED);
>> +       }
>> +
>> +       /*
>>          * For the other attributes, we acquire the inode lock and
>>          * first do an error checking pass.
>>          */
>> @@ -451,17 +469,6 @@
>>          * Truncate file.  Must have write permission and not be a 
>> directory.
>>          */
>>         if (mask & XFS_AT_SIZE) {
>> -               /* Short circuit the truncate case for zero length 
>> files */
>> -               if ((vap->va_size == 0) &&
>> -                  (ip->i_size == 0) && (ip->i_d.di_nextents == 0)) {
>> -                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
>> -                       lock_flags &= ~XFS_ILOCK_EXCL;
>> -                       if (mask & XFS_AT_CTIME)
>> -                               xfs_ichgtime(ip, XFS_ICHGTIME_MOD | 
>> XFS_ICHGTIME_CHG);
>> -                       code = 0;
>> -                       goto error_return;
>> -               }
>> -
>>                 if (VN_ISDIR(vp)) {
>>                         code = XFS_ERROR(EISDIR);
>>                         goto error_return;
 >
> This misses setting the access and changed times which still need to be 
> touched even if the file's already zero bytes. How about this:
> (noting that open.c/do_truncate uses XFS_AT_SIZE | XFS_AT_CTIME)
> 

Well, if XFS_AT_CTIME was set then my patch wouldn't
return straight away and would continue processing the mask fields.
And I was presuming that the times would be set doing this.

However, it doesn't look quite so simple and consistent:

* going down the normal (non-short-circuit) path for AT_SIZE,
   it doesn't test for CTIME but rather just does:

       /*
        * Have to do this even if the file's size doesn't change.
        */
       timeflags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;

   And yet our short-circuit case only does it if AT_CTIME is set.
   Doesn't look consistent to me.

* So what in the vfs would call it...

   -----------------------
   open(O_TRUNC)...

   int may_open(struct nameidata *nd, int acc_mode, int flag)
   {

         if (flag & O_TRUNC) {
   ...
                         error = do_truncate(dentry, 0, ATTR_MTIME|ATTR_CTIME, NULL);

   ------------------------

   static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
   {
         if (!error)
                 error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);

   ------------------------

   This is NOR the case for
   * do_sys_truncate() and
   * do_coredump(),
   however, which send in zero for those bits.
   Not to mention other ways to get to these calls.

   Anyway, open(O_TRUNC) will be a good candidate for the optimization
   by the looks of it as it always trunc's to zero.

   So in the 2 truncate cases, it is setting MTIME and CTIME.

   And how is MTIME handled in the xfs code...

         /*
          * Change file access or modified times.
          */
         if (mask & (XFS_AT_ATIME|XFS_AT_MTIME)) {
                 if (mask & XFS_AT_ATIME) {
                         ip->i_d.di_atime.t_sec = vap->va_atime.tv_sec;
                         ip->i_d.di_atime.t_nsec = vap->va_atime.tv_nsec;
                         ip->i_update_core = 1;
                         timeflags &= ~XFS_ICHGTIME_ACC;
                 }
                 if (mask & XFS_AT_MTIME) {
                         ip->i_d.di_mtime.t_sec = vap->va_mtime.tv_sec;
                         ip->i_d.di_mtime.t_nsec = vap->va_mtime.tv_nsec;
                         timeflags &= ~XFS_ICHGTIME_MOD;
                         timeflags |= XFS_ICHGTIME_CHG;
                 }
                 if (tp && (flags & ATTR_UTIME))
                         xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
         }

         /*
          * Send out timestamp changes that need to be set to the
          * current time.  Not done when called by a DMI function.
          */
         if (timeflags && !(flags & ATTR_DMI))
                 xfs_ichgtime(ip, timeflags);

   And note that MTIME, will actually turn on the timeflags for XFS_ICHGTIME_CHG,
   which is the time associated with XFS_AT_CTIME.

   So for the 2 do_truncate calls paths,
   it will set the mtime based
   on the va_mtime and set the ctime based on current time (nanotime()).
   Our shortcut is setting both to current time.
   I don't know if anyone really cares.


I don't like all these inconsistencies.
One way to reduce inconsistencies is to allow code to go thru
common paths so we can do the same strange thing in the one spot ;-)

It looks like in the AT_SIZE, we should always set those timeflags
irrespective of AT_CTIME.

BTW, your locking looks wrong - it appears you don't unlock when the
file is non-zero size.

--Tim


> --- xfs_vnodeops.c      2007-09-04 15:57:40.000000000 +0100
> +++ /tmp/xfs_vnodeops.c 2007-10-30 17:11:32.000000000 +0000
> @@ -378,6 +378,24 @@
>                        return (code);
>        }
> 
> +
> +       if ((mask & XFS_AT_SIZE) && (vap->va_size == 0)) {
> +
> +               /* Short circuit the truncate case for zero length files */
> +
> +               xfs_ilock(ip, XFS_ILOCK_EXCL);
> +               if ((ip->i_d.di_size == 0) && (ip->i_d.di_nextents == 0)) {
> +                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +                       if (mask & XFS_AT_CTIME)
> +                               xfs_ichgtime(ip, 
> XFS_ICHGTIME_MOD|XFS_ICHGTIME_CHG);
> +                       mask &= ~(XFS_AT_SIZE|XFS_AT_CTIME);
> +                       if (mask == 0) {
> +                               code = 0;
> +                               goto error_return;
> +                       }
> +               }
> +       }
> +
>        /*
>         * For the other attributes, we acquire the inode lock and
>         * first do an error checking pass.
> @@ -528,17 +546,6 @@
>         * Truncate file.  Must have write permission and not be a 
> directory.
>         */
>        if (mask & XFS_AT_SIZE) {
> -               /* Short circuit the truncate case for zero length files */
> -               if ((vap->va_size == 0) &&
> -                  (ip->i_d.di_size == 0) && (ip->i_d.di_nextents == 0)) {
> -                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
> -                       lock_flags &= ~XFS_ILOCK_EXCL;
> -                       if (mask & XFS_AT_CTIME)
> -                               xfs_ichgtime(ip, XFS_ICHGTIME_MOD | 
> XFS_ICHGTIME_CHG);
> -                       code = 0;
> -                       goto error_return;
> -               }
> -
>                if (vp->v_type == VDIR) {
>                        code = XFS_ERROR(EISDIR);
>                        goto error_return;

  reply	other threads:[~2007-11-02  1:11 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-28 14:36 bug: truncate to zero + setuid Roger Willcocks
2007-10-29  0:54 ` Tim Shimmin
2007-10-29 18:56   ` Roger Willcocks
2007-10-30  4:06     ` Timothy Shimmin
2007-10-30 17:28       ` Roger Willcocks
2007-11-02  1:11         ` Timothy Shimmin [this message]
2007-11-04 11:59           ` Roger Willcocks
2007-11-08  3:13             ` Timothy Shimmin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=472A7940.5070800@sgi.com \
    --to=tes@sgi.com \
    --cc=roger@filmlight.ltd.uk \
    --cc=xfs@oss.sgi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox