linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Viacheslav Dubeyko <slava@dubeyko.com>
To: Al Viro <viro@ZenIV.linux.org.uk>
Cc: "Ernesto A. Fernández" <ernesto.mnd.fernandez@gmail.com>,
	linux-fsdevel@vger.kernel.org,
	"Andrew Morton" <akpm@linux-foundation.org>
Subject: Re: [PATCH 1/2] hfsplus: update timestamps on truncate()
Date: Wed, 17 Oct 2018 19:09:36 -0700	[thread overview]
Message-ID: <1539828576.3188.35.camel@slavad-ubuntu-14.04> (raw)
In-Reply-To: <20181015212416.GY32577@ZenIV.linux.org.uk>

On Mon, 2018-10-15 at 22:24 +0100, Al Viro wrote:
> On Mon, Oct 15, 2018 at 02:02:01PM -0700, Viacheslav Dubeyko wrote:
> > > 1) unlink() finds the victim and locks it
> > > 
> > > 2) in hfsplus_file_release():
> > >         if (atomic_dec_and_test(&HFSPLUS_I(inode)->opencnt)) {
> > > got to 0
> > >                 inode_lock(inode);
> > > block waiting for unlink
> > > 
> > > 3) open() finds the sucker in dcache and hits hfsplus_file_open(), where
> > > we do
> > >         atomic_inc(&HFSPLUS_I(inode)->opencnt);
> > > and now opencnt is 1.
> > > 
> > > 4) on the unlink side:
> > >         if (inode->i_ino == cnid &&
> > >             atomic_read(&HFSPLUS_I(inode)->opencnt)) {
> > >                 str.name = name;
> > >                 str.len = sprintf(name, "temp%lu", inode->i_ino);
> > >                 res = hfsplus_rename_cat(inode->i_ino,
> > >                                          dir, &dentry->d_name,
> > >                                          sbi->hidden_dir, &str);
> > >                 if (!res) {
> > >                         inode->i_flags |= S_DEAD;
> > >                         drop_nlink(inode);
> > >                 }
> > >                 goto out;
> > >         }
> > > nlink is zero now, the sucker got renamed and marked S_DEAD
> > > 
> > > 5) ->release() finally got through inode_lock() and
> > >                 hfsplus_file_truncate(inode);
> > >                 if (inode->i_flags & S_DEAD) {
> > >                         hfsplus_delete_cat(inode->i_ino,
> > >                                            HFSPLUS_SB(sb)->hidden_dir, NULL);
> > >                         hfsplus_delete_inode(inode);
> > >                 }
> > >                 inode_unlock(inode);
> > > ... and now we have killed everything we used to have associated with that
> > > inode on disk.  While it's still open.  What's to stop CNID to be reused,
> > > etc. and what's to preserve sanity in that situation?
> > >

The hfsplus_link() increments CNID always [1] and never tries to reuse
the same CNID. HFS+ specification [2] declares that CNID can be to wrap
around and be reused - "HFS Plus volumes now allow CNID values to wrap
around and be reused. The kHFSCatalogNodeIDsReusedBit in the attributes
field of the volume header is set to indicate when CNID values have
wrapped around and been reused. When kHFSCatalogNodeIDsReusedBit is set,
the nextCatalogID field is no longer required to be greater than any
existing CNID. When kHFSCatalogNodeIDsReusedBit is set, nextCatalogID
may still be used as a hint for the CNID to assign to newly created
files or directories, but the implementation must verify that CNID is
not currently in use (and pick another value if it is in use). When CNID
number nextCatalogID is already in use, an implementation could just
increment nextCatalogID until it finds a CNID that is not in use. If
nextCatalogID overflows to zero, kHFSCatalogNodeIDsReusedBit must be set
and nextCatalogID set to kHFSFirstUserCatalogNodeID (to avoid using any
reserved CNID values)". However, HFS+ driver in Linux kernel believes
that u32 type is so huge that never will be exhausted.

I try to show the whole picture:

1) unlink() finds the victim and locks it.

2) in hfsplus_file_release():
         if (atomic_dec_and_test(&HFSPLUS_I(inode)->opencnt)) {
 got to 0
                 inode_lock(inode);
 block waiting for unlink

3) open() finds the sucker in dcache and hits hfsplus_file_open(), where
 we do
         atomic_inc(&HFSPLUS_I(inode)->opencnt);
 and now opencnt is 1.

4) on the unlink side:
if opencnt > 0 then we rename the file by temp<inode_id>, move into the
hidden_dir, mark as S_DEAD and go out. It means that if file is opened
currently then we cannot simply delete the file. The file converted in
the temporary state and the responsibility to delete the file is
delegated to the thread that opened the file during hfsplus_release(). 

5) ->release() finally got through inode_lock()
It looks like that hfsplus_release() has to analyze opencnt more
properly. I suppose that it should look something like this:

    inode_lock(inode);
    mutex_lock(&sbi_vh_mutex);

    if (atomic_dec_and_test(&HFSPLUS_I(inode)->opencnt)) {
         hfsplus_file_truncate(inode);
         if (inode->i_flags & S_DEAD) {
             hfsplus_delete_cat(inode->i_ino,
                                HFSPLUS_SB(sb)->hidden_dir, NULL);
             hfsplus_delete_inode(inode);
         }
    }

    mutex_unlock(&sbi_vh_mutex);
    inode_unlock(inode);

What do you think? Do I miss something?

Thanks,
Vyacheslav Dubeyko.

[1] https://elixir.bootlin.com/linux/latest/source/fs/hfsplus/dir.c#L342
[2] https://developer.apple.com/library/archive/technotes/tn/tn1150.html

  parent reply	other threads:[~2018-10-18 10:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-12  4:22 [PATCH 1/2] hfsplus: update timestamps on truncate() Ernesto A. Fernández
2018-10-12  4:23 ` [PATCH 2/2] hfs: update timestamp " Ernesto A. Fernández
2018-10-12 21:57   ` Viacheslav Dubeyko
2018-10-12 21:57 ` [PATCH 1/2] hfsplus: update timestamps " Viacheslav Dubeyko
2018-10-13  2:42   ` Al Viro
2018-10-15 21:02     ` Viacheslav Dubeyko
2018-10-15 21:24       ` Al Viro
2018-10-17  2:01         ` Viacheslav Dubeyko
2018-10-18  2:09         ` Viacheslav Dubeyko [this message]
2018-10-16 23:15     ` Ernesto A. Fernández

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=1539828576.3188.35.camel@slavad-ubuntu-14.04 \
    --to=slava@dubeyko.com \
    --cc=akpm@linux-foundation.org \
    --cc=ernesto.mnd.fernandez@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=viro@ZenIV.linux.org.uk \
    /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;
as well as URLs for NNTP newsgroup(s).