tag 635683 + patch
thanks

Hi,

On Fri, 12 Aug 2011, Jonathan Nieder wrote:
> Ok, here's a patch with better behavior.  On Linux and similar platforms,
> it just opens the file for reading.  If fsync fails with errno == EBADF,
> it falls back to opening for writing, and if that fails with errno ==
> ETXTBSY, it skips the fsync under the assumption that the file has already
> been fsynced.

I thought the same at first but all this is just wrong. If you look
carefully, you'll notice that only real files are fsync()ed and hardlinks
are not (look where fnnf_deferred_fsync is added to the flags).

In the tar archive, the first file in a set of hardlinked files appears as
a normal file and the others are then marked as hardlinks of the first
file.

What broke dpkg here is the the fact that hardlinks do not use
deferred renames and are thus put into places before the "target file"
is fsync()ed and renamed. Thus the minimal fix is this one:

> --- a/src/archives.c
> +++ b/src/archives.c
> @@ -851,7 +851,8 @@ tarobject(void *ctx, struct tar_entry *ti)
>     * in .dpkg-new.
>     */
>  
> -  if (ti->type == tar_filetype_file || ti->type == tar_filetype_symlink) {
> +  if (ti->type == tar_filetype_file || ti->type == tar_filetype_symlink ||
> +      ti->type == tar_filetype_hardlink) {
>      nifd->namenode->flags |= fnnf_deferred_rename;
>  
>      debug(dbg_eachfiledetail, "tarobject done and installation deferred");

But from a design point of view, it just seems wrong to not have all files
treated the same. IMO we should be using deferred rename for all files except
directories.

Then one can wonder whether we ought to do something to ensure other types
of files are written on the disk too... and whether adding a fsync() on hardlink
brings anything. Since the hardlinks all point to the same inode, I guess
a fsync() on the hardlink doesn't add anything, the supplementary information
is just the availability of the same file on a different path and to
ensure persistency of this it's the fsync() on the directory that we would
need.

I have attached my suggested patch. It's tested and I confirm that it
fixes this issue.

Cheers,
-- 
Raphaël Hertzog ◈ Debian Developer

Follow my Debian News ▶ http://RaphaelHertzog.com (English)
                      ▶ http://RaphaelHertzog.fr (Français)
commit 8f91f1da8defc4d8464fda8b100f966f655dceeb
Author: Raphaël Hertzog <hert...@debian.org>
Date:   Fri Aug 12 21:23:58 2011 +0200

    dpkg: use deferred rename for all files except directories
    
    The decision to defer fsync/rename only of normal files and to
    keep renaming the other directly types during the unpack
    (commit 9cd41fdda1c27169c52d73b3b3ce71991d724994) has been
    wrong first for the symlinks (commit
    a766f501f6da46aca070c315e6429e163d188202) and we discover now
    that it's also wrong for hardlinks: due to this change hardlinks
    are put into place before the "target" file. This opens up the
    possibility that the file gets executed (via the hardlink) before
    the deferred fsync/rename which then fails with ETXTBSY.
    
    Instead of making yet another exception for hardlinks, it's time
    to decide that deferred rename is the norm and only directories
    should be exempted from this.
    
    This change also means that when we create hardlinks we can assume the
    target file is a .dpkg-new one and we can get rid of the check on
    fnnf_deferred_rename to compute the correct path.
    
    Reported-by: Niko Tyni <nt...@debian.org>

diff --git a/debian/changelog b/debian/changelog
index e875ca6..7c6343d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -98,6 +98,9 @@ dpkg (1.16.1) UNRELEASED; urgency=low
   * Fix possible segfault of dpkg in findbreakcycle(). LP: #733414
   * dpkg-source now properly cleans up the temporary tarball generated for
     native formats in case of unexpected interruption. Closes: #631494
+  * Fix dpkg to defer rename of hardlinks just like normal files. Otherwise
+    we might end up trying to fsync() a file that is already used and
+    fail with ETXTBSY. Closes: #635683
 
   [ Guillem Jover ]
   * Install deb-src-control(5) man pages in dpkg-dev. Closes: #620520
diff --git a/src/archives.c b/src/archives.c
index f060e70..c8d80aa 100644
--- a/src/archives.c
+++ b/src/archives.c
@@ -764,8 +764,7 @@ tarobject(void *ctx, struct tar_entry *ti)
     varbuf_add_char(&hardlinkfn, '/');
     linknode = findnamenode(ti->linkname, 0);
     varbuf_add_str(&hardlinkfn, namenodetouse(linknode, tc->pkg)->name);
-    if (linknode->flags & fnnf_deferred_rename)
-      varbuf_add_str(&hardlinkfn, DPKGNEWEXT);
+    varbuf_add_str(&hardlinkfn, DPKGNEWEXT);
     varbuf_end_str(&hardlinkfn);
     if (link(hardlinkfn.buf,fnamenewvb.buf))
       ohshite(_("error creating hard link `%.255s'"), ti->name);
@@ -851,7 +850,7 @@ tarobject(void *ctx, struct tar_entry *ti)
    * in .dpkg-new.
    */
 
-  if (ti->type == tar_filetype_file || ti->type == tar_filetype_symlink) {
+  if (ti->type != tar_filetype_dir) {
     nifd->namenode->flags |= fnnf_deferred_rename;
 
     debug(dbg_eachfiledetail, "tarobject done and installation deferred");

Reply via email to