Subject: bug#78623: cp --preserve=xattr copies attr as well as xattr which breaks cross-FS copy

From: Gael Donval <[email-masked]>
Date: Fri, 30 May 2025 09:24:06 +0000

On Fri, 2025-05-30 at 10:02 +0100, Gaël Donval wrote:
> On Thu, 2025-05-29 at 12:53 +0100, Gaël Donval wrote:
> > > On 29/05/2025 10:56, Gael Donval wrote:
> > > > On Thu, 2025-05-29 at 10:10 +0100, Pádraig Brady wrote:
> > > > > On 28/05/2025 16:14, Gael Donval wrote:
> > > > > > Dear list,
> > > > > > 
> > > > > > We've unearth an odd behaviour in cp: `cp --preserve=xattr` tries to copy both attributes of the chattr kind and extended attributes of the getfattr kind with apparently no way to disable either one of them (it's all or nothing). This is problematic in tools like mkosi where non-filesystem-specific xattributes need to be preserved whilst FS-specific attributes must be discarded for cross-filesystem support.
> > > > > > 
> > > > > > I have added a MWE at the end of this email after my signature: it creates two raw partitions as files (one as XFS, one as BTRFS), mounts them in local folders and creates 3 files in the BTRFS partitions later altered before copy.
> > > > > > 
> > > > > > Referring to the script, we think there should be an option to copy files foo (no-attr), bar (setfattr) and baz (chattr), keeping the setfattr's xattr and discarding chattr's attr. Looking at the code, it seems like cp eventually defers the actual attribute copying libattr, which seems to handle both, but separately (which is what we want).
> > > > > > 
> > > > > > Would it make sense to add a separate `attr` preserve value for the chattr case and keep `xattr` for getfattr case?
> > > > > 
> > > > > I've not looked in detail at your case,
> > > > > but it's worth noting that /etc/xattr.conf
> > > > > gives the facility to skip copying certain xattrs.
> > > > > Does that suffice to handle your issue?
> > > > 
> > > > As far as I can tell, xattr.conf cannot be used to ignore normal file
> > > > attributes (as opposed to extended attributes), can it? We need a way
> > > > to copy extended attributes without copying normal file attributes.
> > > 
> > > Well there is no code in coreutils to copy "chattr" attributes.
> > > It was discussed previously, but we decided not to support those
> > > due to the incompat issues you're hitting.
> > 
> > It does copy them though. If it's not the intent of `preserve=xattr`,
> > I'm afraid it's a bug. My reproducer makes it obvious that it tries to
> > copy them. If you change `mkfs.xfs` to `mkfs.btrfs`, it will all
> > succeed and you'll see chattr attributes are copied. If your remove `--
> > preserver=xattr`, you'll see chattr attributes are not copied anymore. 
> > > 
> > > That version of cp are you using?
> > 
> > cp (GNU coreutils) 9.7
> > 
> > > 
> > > strace may be instructive as to that is reading/writing these chattrs
> > > (this is done with ioctl FS_IOC_GETFLAGS IIRC).
> > 
> > I might be wrong but it looks like everything xattr-related is deferred
> > to libattr. 
> > I'll investigate that tomorrow!
> 
> Ok, I've done that and you were right. I guess BTRFS implements its
> file attributes as extended attributes. The relevant part of the strace
> is this:
> 
> openat(AT_FDCWD, "btrfs/baz", O_RDONLY) = 4
> openat(3, "baz", O_WRONLY|O_CREAT|O_EXCL, 0644) = 5
> flistxattr(4, NULL, 0)                  = 18
> flistxattr(4, "btrfs.compression\0", 18) = 18
> openat(AT_FDCWD, "/etc/xattr.conf", O_RDONLY) = 6
> read(6, "# /etc/xattr.conf\n#\n# Format:\n# "..., 4096) = 622
> fgetxattr(4, "btrfs.compression", NULL, 0) = 4
> fgetxattr(4, "btrfs.compression", "zlib", 4) = 4
> fsetxattr(5, "btrfs.compression", "zlib", 4, 0) = -1 EOPNOTSUPP (Operation not supported)
> 
> and updating /etc/xattr.conf to ignore it does work.
> 
> I can't see any way to tweak this behaviour at command-line level yet
> the behaviour can be triggered by normal users: would it make sense to
> add a flag to filter xattr out at CLI level?

Or perhaps introduce a `preserve=xattr-safe` alternative that ignores
all but security, system, trusted and user namespaces? It won't compose
well with `all` but it's not introducing a new flag and works better
semantically.

Gaël 
> Cheers,
> Gaël
> > Gaël
> > > cheers,
> > > Pádraig