Package: dump
Version: 0.4b47-4+~tjw12r5
Severity: important

Dear Maintainer,

Many years ago I reported a bug with verifying attributes on files where
the count was sometimes wrong. You fixed this (Thanks!)

However, it turns out the same bug is present for directories too.

Unfortunately, the fix for files meant that directories *verified*
correctly but did not restore correctly.

The attached patch *replaces* the xattr_verify patch currently in the
package and includes fix for the directory attributes which now both
restore and verify successfully.

(I haven't actually tested failure cases for the verify yet - i.e.
adding, removing or modifying an attribute between backup and verify)

-- System Information:
Debian Release: 12.7
  APT prefers stable-security
  APT policy: (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-25-amd64 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: sysvinit (via /sbin/init)

Versions of packages dump depends on:
ii  libblkid1     2.38.1-5+deb12u1
ii  libbz2-1.0    1.0.8-5+b1
ii  libc6         2.36-9+deb12u8
ii  libcom-err2   1.47.0-2
ii  libext2fs2    1.47.0-2
ii  liblzo2-2     2.10-2
ii  libreadline8  8.2-1.3
ii  libselinux1   3.4-1+b6
ii  tar           1.34+dfsg-1.2+deb12u1
ii  zlib1g        1:1.2.13.dfsg-1

dump recommends no packages.

dump suggests no packages.

-- no debconf information
diff -urN dump-0.4b47.orig/restore/dirs.c dump-0.4b47/restore/dirs.c
--- dump-0.4b47.orig/restore/dirs.c     2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/restore/dirs.c  2022-05-03 10:02:27.000000000 +0000
@@ -181,7 +181,7 @@
        struct inotab *itp;
        struct direct nulldir;
        int fd;
-       char xattr[XATTR_MAXSIZE];
+       char xattr[XATTR_MAXSIZE*2] = {0};
        int xattr_found = 0;
        _Bool is_tmp = command != 'r' && command != 'R';
        dump_ino_t ino;
@@ -248,7 +248,7 @@
                                skipfile();
                                break;
                        case EXT_XATTR:
-                               if (readxattr(xattr) == GOOD)
+                               if (readxattr(xattr+xattr_found*XATTR_MAXSIZE) 
== GOOD)
                                        xattr_found = 1;
                                break;
                        }
@@ -667,14 +667,14 @@
        }
        clearerr(mf);
        for (;;) {
-               char xattr[XATTR_MAXSIZE];
+               char xattr[XATTR_MAXSIZE*2];
                if (fread((char *)&node, sizeof(struct modeinfo), 1, mf) != 1) {
                        if (feof(mf))
                                break;
                        err(1, "fread");
                }
                if (node.xattr) {
-                       if (fread(xattr, XATTR_MAXSIZE, 1, mf) != 1) {
+                       if (fread(xattr, XATTR_MAXSIZE*2, 1, mf) != 1) {
                                if (feof(mf))
                                        break;
                                err(1, "fread");
@@ -701,8 +701,13 @@
                                warn("chown");
                        (void) chmod(cp, node.mode);
                        utimes(cp, node.timep);
-                       if (node.xattr)
+                       if (node.xattr) {
                                xattr_extract(cp, xattr);
+                               char* xattr2 = xattr + XATTR_MAXSIZE;
+                               if (*(uint32_t*)xattr2 != 0) {
+                                       xattr_extract(cp, xattr2);
+                               }
+                       }
                        ep->e_flags &= ~NEW;
                        if (node.flags)
 #ifdef __linux__
@@ -746,14 +751,14 @@
        }
        clearerr(mf);
        for (;;) {
-               char xattr[XATTR_MAXSIZE];
+               char xattr[XATTR_MAXSIZE*2];
                if (fread((char *)&node, sizeof(struct modeinfo), 1, mf) != 1) {
                        if (feof(mf))
                                break;
                        err(1, "fread");
                }
                if (node.xattr) {
-                       if (fread(xattr, XATTR_MAXSIZE, 1, mf) != 1) {
+                       if (fread(xattr, XATTR_MAXSIZE*2, 1, mf) != 1) {
                                if (feof(mf))
                                        break;
                                err(1, "fread");
@@ -805,14 +810,32 @@
                                }
                        }
 #endif
+                       int xattr_count_all = 0;
                        if (node.xattr) {
+                               int xattr_count_thisblock;
                                if (xattr_compare(cp, xattr) == FAIL)
                                        do_compare_error;
+                               xattr_count(xattr, &xattr_count_thisblock);
+                               xattr_count_all += xattr_count_thisblock;
+                               char* xattr2 = xattr + XATTR_MAXSIZE;
+                               if (*(uint32_t*)xattr2 != 0) {
+                                       xattr_extract(cp, xattr2);
+                                       xattr_count(xattr2, 
&xattr_count_thisblock);
+                                       xattr_count_all += 
xattr_count_thisblock;
+                               }
                        }
                        else {
                                if (xattr_compare(cp, NULL) == FAIL)
                                        do_compare_error;
                        }
+                       int count_file = xattr_count_file(cp);
+                       if (count_file < 0) {
+                               do_compare_error;
+                       } else if (count_file != xattr_count_all) {
+                               fprintf(stderr, "%s: directory does not have 
the same number of attributes (%d) as the tape (%d).\n",
+                                                               cp, count_file, 
xattr_count_all );
+                               do_compare_error;
+                       }
                        ep->e_flags &= ~NEW;
                }
        }
@@ -925,7 +948,7 @@
        if ( fwrite((char *)&node, sizeof(struct modeinfo), 1, mf) != 1 )
                err(1,"cannot write to file %s", modefile);
        if (xattr)
-               if ( fwrite(xattr, XATTR_MAXSIZE, 1, mf) != 1 )
+               if ( fwrite(xattr, XATTR_MAXSIZE*2, 1, mf) != 1 )
                        err(1,"cannot write to file %s", modefile);
 }
 
diff -urN dump-0.4b47.orig/restore/extern.h dump-0.4b47/restore/extern.h
--- dump-0.4b47.orig/restore/extern.h   2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/restore/extern.h        2022-05-03 10:02:27.000000000 +0000
@@ -154,3 +154,5 @@
 int    readxattr (char *);
 int    xattr_compare (char *, char *);
 int    xattr_extract (char *, char *);
+int xattr_count_file (char *);
+int xattr_count (char *, int *);
diff -urN dump-0.4b47.orig/restore/tape.c dump-0.4b47/restore/tape.c
--- dump-0.4b47.orig/restore/tape.c     2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/restore/tape.c  2022-05-03 10:02:27.000000000 +0000
@@ -1711,39 +1711,63 @@
 }
 #endif /* !COMPARE_ONTHEFLY */
 
+/* collect both extended attribute tape blocks, then
+       compare the attributes */
 static void
 compareattr(char *name)
 {
        int xattr_done = 0;
+       int xattr_count_all = 0;
 
        while (spcl.c_flags & DR_EXTATTRIBUTES) {
-               switch (spcl.c_extattributes) {
-               case EXT_MACOSFNDRINFO:
-                       msg("MacOSX not supported for comparision in this 
version, skipping\n");
-                       skipfile();
-                       break;
-               case EXT_MACOSRESFORK:
-                       msg("MacOSX not supported for comparision in this 
version, skipping\n");
-                       skipfile();
-                       break;
-               case EXT_XATTR: {
-                       char xattr[XATTR_MAXSIZE];
+                       switch (spcl.c_extattributes) {
+                               case EXT_MACOSFNDRINFO:
+                                               msg("MacOSX not supported for 
comparision in this version, skipping\n");
+                                               skipfile();
+                                               break;
+                               case EXT_MACOSRESFORK:
+                                               msg("MacOSX not supported for 
comparision in this version, skipping\n");
+                                               skipfile();
+                                               break;
+                               case EXT_XATTR: {
+                                               char xattr[XATTR_MAXSIZE];
+                                               int xattr_count_thisblock = 0;
 
-                       if (readxattr(xattr) == GOOD) {
-                               if (xattr_compare(name, xattr) == FAIL)
-                                       do_compare_error;
-                               xattr_done = 1;
-                       }
-                       else
-                               do_compare_error;
-                       break;
-               }
+                                               /* in-inode xattr record comes 
first, if present at all; then block xattr */
+                                               if (readxattr(xattr) == GOOD) {
+                                                       /* this returns fail if 
the buffer contains different attributes
+                                                                       than 
the file on disk; note that it mustn't return fail if the file on disk has more 
attributes! */
+                                                       if (xattr_compare(name, 
xattr) == FAIL)
+                                                                       
do_compare_error;
+
+                                                       xattr_count(xattr, 
&xattr_count_thisblock);
+                                                       xattr_count_all += 
xattr_count_thisblock;
+                                                       xattr_done = 1;
+                                               }
+                                               else
+                                                       do_compare_error;
+                                               break;
+                               }
                default:
                        msg("unexpected inode extension %ld, skipping\n", 
spcl.c_extattributes);
                        skipfile();
                        break;
                }
        }
+
+       /* cover the case of two xattr sources, the contents of which matched 
the file on disk:
+                       the only uncovered case left is if there re more 
attributes on disk than observed on tape */
+       if (xattr_done) {
+                       int count_file = xattr_count_file(name);
+                       if (count_file < 0) {
+                               do_compare_error;
+                       } else if (count_file != xattr_count_all) {
+                               fprintf(stderr, "%s: file does not have the 
same number of attributes (%d) as the tape (%d).\n",
+                                                               name, 
count_file, xattr_count_all );
+                               do_compare_error;
+                       }
+       }
+       /* no attrs whatsoever? file must have none then as well */
        if (!xattr_done && xattr_compare(name, NULL) == FAIL)
                do_compare_error;
 }
diff -urN dump-0.4b47.orig/restore/xattr.c dump-0.4b47/restore/xattr.c
--- dump-0.4b47.orig/restore/xattr.c    2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/restore/xattr.c 2022-05-03 10:02:27.000000000 +0000
@@ -123,7 +123,7 @@
 static int xattr_cb_set (char *, char *, int, int, void *);
 static int xattr_cb_compare (char *, char *, int, int, void *);
 static int xattr_verify (char *);
-static int xattr_count (char *, int *);
+int xattr_count (char *, int *);
 static int xattr_walk (char *, int (*)(char *, char *, int, int, void *), void 
*);
 
 #define POSIX_ACL_XATTR_VERSION 0x0002
@@ -373,7 +373,7 @@
 #endif
                valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE);
                if (valuesz < 0) {
-                       warn("%s: EA compare lgetxattr failed\n", path);
+                       warn("%s: EA compare lgetxattr for %s failed\n", path, 
name);
                        return FAIL;
                }
 #ifdef TRANSSELINUX                    /*GAN6May06 SELinux MLS */
@@ -381,8 +381,9 @@
 #endif
 
        if (valuesz != valuelen || memcmp(value, valuef, valuelen)) {
+               valuef[valuesz] = '\0';
                /* GAN24May06: show name and new value for user to compare */
-               fprintf(stderr, "%s: EA %s:%s value changed to %s\n", path, 
name, value, valuef);
+               fprintf(stderr, "%s: EA %s value changed from %s on tape to %s 
on disk\n", path, name, value, valuef);
                return FAIL;
        }
 
@@ -429,7 +430,7 @@
        return GOOD;
 }
 
-static int
+int
 xattr_count(char *buffer, int *count)
 {
        struct ext2_xattr_entry *entry;
@@ -493,6 +494,7 @@
                size = entry->e_value_size;
 
                memcpy(value, buffer + VALUE_OFFSET(buffer, entry), size);
+               value[size]='\0';
 
                if (convertacl) {
                        struct posix_acl *acl;
@@ -540,43 +542,58 @@
        return GOOD;
 }
 
-int
-xattr_compare(char *path, char *buffer)
+/* return count of the xattrs on the given file or FAIL if failed */
+int xattr_count_file(char *path)
 {
-       int countf, countt;
+       int size, count = 0;
        char *names = NULL, *end_names, *name;
 
-       countf = llistxattr(path, NULL, 0);
-       if (countf < 0) {
-               warn("%s: llistxattr failed", path);
-               return FAIL;
+       /* return number of bytes a/v */
+       size = llistxattr(path, NULL, 0);
+       if (size < 0) {
+                       warn("%s: llistxattr failed", path);
+                       return FAIL;
        }
 
-       names = malloc(countf + 1);
-       if (!names) {
-               warn("%s: llistxattr failed", path);
-               return FAIL;
+       names = malloc(size + 1);
+       if (!names)
+       {
+                       warn("%s: malloc failed", path);
+                       return FAIL;
        }
 
-       countf = llistxattr(path, names, countf);
-       if (countf < 0) {
-               warn("%s: llistxattr failed", path);
-               free(names);
-               return FAIL;
+       /* return nr of bytes and park data in names array */
+       size = llistxattr(path, names, size);
+       if (size < 0)
+       {
+                       warn("%s: llistxattr failed", path);
+                       free(names);
+                       return FAIL;
        }
 
-       names[countf] = '\0';
-       end_names = names + countf;
+       names[size] = '\0';
+       end_names = names + size;
 
-       countf = 0;
-       for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
-               if (!*name)
-                       continue;
-               countf++;
+       for (name = names; name != end_names; name = strchr(name, '\0') + 1)
+       {
+                       if (!*name)
+                               continue;
+                       count++;
        }
-
        free(names);
 
+       return count;
+}
+
+int
+xattr_compare(char *path, char *buffer)
+{
+       int countf, countt;
+
+       countf = xattr_count_file(path); /* already warns on failure */
+       if (countf < 0)
+                       return FAIL;
+
        if (buffer) {
                if (xattr_verify(buffer) == FAIL)
                        return FAIL;
@@ -587,10 +604,20 @@
        else
                countt = 0;
 
-       if (countf != countt) {
-               fprintf(stderr, "%s: EA count changed from %d to %d\n", path, 
countt, countf);
+       /* with ext4 there may be two tape blocks for attrs: one from the 
actual inode, plus
+               one extra filesystem block. in this case countf must be at 
least >= countt;
+               verification of that aspect is delayed and left to compareattr
+               which loops over the different blocks */
+
+       /* nothing on tape but something on disk? sure fail. ditto for more on 
tape than on disk. */
+       if (countf > 0 && !countt)
+       {
+               fprintf(stderr, "%s: file does not have the same number of 
attributes (%d) as the tape (%d).\n",
+                                               path, countf, countt);
                return FAIL;
        }
+       if (countf < countt)
+               return FAIL;                                                    
        /* warning left to compareattr */
 
        if (!buffer)
                return GOOD;

Reply via email to