Jean-Francois Dockes via GNU coreutils Bug Reports
<[email protected]> writes:

> Paul Eggert writes:
>  > On 12/11/23 12:03, Abraham S.A.H. via GNU coreutils Bug Reports wrote:
>  > > a sane default behaviour regarding extended attributes in mv and others?
>  > 
>  > What's wrong with the default behavior in current GNU mv? Please give a 
>  > specific example (specify platform, filesystems, mv version, etc.).
>
> mv still silently discards the extended attributes when asked to move a file 
> from
> e.g. an ext4 fs to e.g., a VFAT one.
>
> Again: the data is gone. mv lost the data.
>
> For example (on Ubuntu 26.04, but this is probably irrelevant):
>
> hm3$ mv --version
> mv (GNU coreutils) 9.7
> Packaged by Ubuntu (9.7-3ubuntu2)
> Copyright (C) 2025 Free Software Foundation, Inc.
> License GPLv3+: GNU GPL version 3 or later 
> <https://gnu.org/licenses/gpl.html>.
> This is free software: you are free to change and redistribute it.
> There is NO WARRANTY, to the extent permitted by law.
>
> Written by Mike Parker, David MacKenzie, and Jim Meyering.
>
> hm3$ getfattr myfile.txt 
> # file: myfile.txt
> user.myattr
>
> hm3$ mv myfile.txt /run/media/dockes/C052-0E64/
>
> hm3$ getfattr /run/media/dockes/C052-0E64/myfile.txt 
>
> hm3$ mv /run/media/dockes/C052-0E64/myfile.txt .
>
> hm3$ getfattr myfile.txt 
>
>
> mv just casually discarding data...
>
> You are asking what's wrong. IMO this is wrong.
>
> If the target file system is full ("No space left on device"), mv keeps the
> original file. It should do the same for an extended attribute problem.

Just to avoid confusing any other readers, I want to clarify that the
"data loss" mentioned here is the extended attributes of the given file,
not the file contents. E.g., see the result of the following commands:

    $ echo a > a && setfattr --name=user.charset --value=utf-8 a \
        && mv a /mnt/vfat-mount/ \
        && getfattr --encoding=text --dump /mnt/vfat-mount/a \
        && cat /mnt/vfat-mount/a
    a

This is because VFAT does not support extended attributes. You can see
the behavior by adding 'strace' before the 'mv' invocation:

    renameat2(AT_FDCWD, "a", AT_FDCWD, "/mnt/vfat-mount/", RENAME_NOREPLACE) = 
-1 EEXIST (File exists)
    openat(AT_FDCWD, "/mnt/vfat-mount/", O_RDONLY|O_PATH|O_DIRECTORY) = 3
    renameat2(AT_FDCWD, "a", 3, "a", RENAME_NOREPLACE) = -1 EXDEV (Invalid 
cross-device link)
    newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0644, st_size=2, ...}, 
AT_SYMLINK_NOFOLLOW) = 0
    newfstatat(3, "a", {st_mode=S_IFREG|0755, st_size=2, ...}, 
AT_SYMLINK_NOFOLLOW) = 0
    geteuid()                               = 0
    renameat(AT_FDCWD, "a", 3, "a")         = -1 EXDEV (Invalid cross-device 
link)
    unlinkat(3, "a", 0)                     = 0
    lgetxattr("a", "security.selinux", "unconfined_u:object_r:admin_home"..., 
255) = 38
    futex(0x7f275cc00580, FUTEX_WAKE_PRIVATE, 2147483647) = 0
    openat(AT_FDCWD, "/proc/thread-self/attr/fscreate", O_RDWR|O_CLOEXEC) = 4
    write(4, "unconfined_u:object_r:admin_home"..., 38) = 38
    close(4)                                = 0
    openat(AT_FDCWD, "a", O_RDONLY|O_NOFOLLOW) = 4
    fstat(4, {st_mode=S_IFREG|0644, st_size=2, ...}) = 0
    openat(3, "a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 5
    ioctl(5, BTRFS_IOC_CLONE or FICLONE, 4) = -1 EXDEV (Invalid cross-device 
link)
    fstat(5, {st_mode=S_IFREG|0755, st_size=0, ...}) = 0
    copy_file_range(4, NULL, 5, NULL, 9223372035781033984, 0) = -1 EXDEV 
(Invalid cross-device link)
    mmap(NULL, 270336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 
= 0x7f275c89d000
    read(4, "a\n", 262144)                  = 2
    write(5, "a\n", 2)                      = 2
    read(4, "", 262144)                     = 0
    munmap(0x7f275c89d000, 270336)          = 0
    utimensat(5, NULL, [{tv_sec=1786821834, tv_nsec=183963469} /* 
2026-08-15T12:23:54.183963469-0700 */, {tv_sec=1786821834, tv_nsec=183963469} 
/* 2026-08-15T12:23:54.183963469-0700 */], 0) = 0
    flistxattr(4, "user.charset\0security.selinux\0", 511) = 30
    openat(AT_FDCWD, "/etc/xattr.conf", O_RDONLY) = 6
    fstat(6, {st_mode=S_IFREG|0644, st_size=817, ...}) = 0
    read(6, "# /etc/xattr.conf\n#\n# Format:\n# "..., 4096) = 817
    read(6, "", 4096)                       = 0
    close(6)                                = 0
    openat(AT_FDCWD, "/usr/lib64/gconv/gconv-modules.cache", 
O_RDONLY|O_CLOEXEC) = 6
    fstat(6, {st_mode=S_IFREG|0644, st_size=27012, ...}) = 0
    mmap(NULL, 27012, PROT_READ, MAP_SHARED, 6, 0) = 0x7f275cc37000
    close(6)                                = 0
    futex(0x7f275cbb26ec, FUTEX_WAKE_PRIVATE, 2147483647) = 0
    fgetxattr(4, "user.charset", "utf-8", 512) = 5
    fsetxattr(5, "user.charset", "utf-8", 5, 0) = -1 EOPNOTSUPP (Operation not 
supported)

Are you suggesting we remove the moved file after fsetxattr fails and
preserve a copy to restore the orignal file that was renamed with its
extended attributes intact? That behavior seems a bit strange to me...
Why not just move the file as the user requested? It seems strange to me
that a user would depend on extended attributes, but use a file system
that does not support them...

Collin



Reply via email to