Skip to content

link fails with EEXIST if hard link with the same name was previously replaced with another file and renamed #4849

Open

Description

@Slava0135

Detailed description

Hello, I am running some tests for different filesystems, including glusterfs. There seems to be a following issue:

  1. Create 2 empty files: file1 and file2.
  2. Create hard link for file2 at ln1.
  3. Replace ln1 with file1 (using rename).
  4. Rename ln1 to ln2.
  5. Try creating hard link for file2 at ln1 (again).

Last operation fails with File exists error (EEXIST).

Mount logs:

[2026-09-24 12:32:52.561182 +0000] W [MSGID: 114031] [client-rpc-fops_v2.c:2406:client4_0_link_cbk] 0-fstest-client-0: remote operation failed. [{source=/file2}, {target=/ln1}, {errno=17}, {error=File exists}] 
[2026-09-24 12:32:52.561195 +0000] W [fuse-bridge.c:1052:fuse_entry_cbk] 0-glusterfs-fuse: 131: LINK() /ln1 => -1 (File exists)

Brick logs:

[2026-09-24 12:32:52.561125 +0000] W [MSGID: 113093] [posix-entry-ops.c:425:posix_set_gfid2path_xattr] 0-fstest-posix: setting gfid2path xattr failed on /mnt/brick/data/ln1: key = trusted.gfid2path.135a277770d12235  [File exists]
[2026-09-24 12:32:52.561153 +0000] I [MSGID: 115062] [server-rpc-fops_v2.c:1173:server4_link_cbk] 0-fstest-server: LINK info [{frame=121}, {LINK_path=/file2}, {gfid_str=41f55308-cf26-4b1f-995f-7d9d162a649a}, {newpar_str=00000000-0000-0000-0000-000000000001}, {resolve2.bname=ln1}, {client=CTX_ID:44dc291f-c90f-4bc3-b19e-86a159975b98-GRAPH_ID:0-PID:1677-HOST:ubuntu-PC_NAME:fstest-client-0-RECON_NO:-0}, {error-xlator=fstest-posix}, {errno=17}, {error=File exists}]

System info

{
        "Hostname" : "ubuntu",
        "StaticHostname" : "ubuntu",
        "PrettyHostname" : null,
        "DefaultHostname" : "localhost",
        "HostnameSource" : "static",
        "IconName" : "computer-vm",
        "Chassis" : "vm",
        "ChassisAssetTag" : null,
        "Deployment" : null,
        "Location" : null,
        "KernelName" : "Linux",
        "KernelRelease" : "7.0.0-34-generic",
        "KernelVersion" : "#34-Ubuntu SMP PREEMPT_DYNAMIC Wed Sep  2 14:29:37 UTC 2026",
        "OperatingSystemPrettyName" : "Ubuntu 26.04.1 LTS",
        "OperatingSystemCPEName" : null,
        "OperatingSystemHomeURL" : "https://www.ubuntu.com/",
        "OperatingSystemReleaseData" : [
                "PRETTY_NAME=Ubuntu 26.04.1 LTS",
                "NAME=Ubuntu",
                "VERSION_ID=26.04",
                "VERSION=26.04.1 LTS (Resolute Raccoon)",
                "VERSION_CODENAME=resolute",
                "ID=ubuntu",
                "ID_LIKE=debian",
                "HOME_URL=https://www.ubuntu.com/",
                "SUPPORT_URL=https://help.ubuntu.com/",
                "BUG_REPORT_URL=https://bugs.launchpad.net/ubuntu/",
                "PRIVACY_POLICY_URL=https://www.ubuntu.com/legal/terms-and-policies/privacy-policy",
                "UBUNTU_CODENAME=resolute",
                "LOGO=ubuntu-logo"
        ],
}

How to reproduce

Tested glusterfs versions: 11.2.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
  int status;

  status = creat("file1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  printf("CREAT: %d\n", status);
  close(status);

  status = creat("file2", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  printf("CREAT: %d\n", status);
  close(status);

  status = link("file2", "ln1");
  printf("LINK: %d\n", status);

  status = rename("file1", "ln1");
  printf("RENAME: %d\n", status);

  status = rename("ln1", "ln2");
  printf("RENAME: %d\n", status);

  status = link("file2", "ln1");
  printf("LINK: %d (%s)\n", status, strerror(errno));
}

// ::Expected::
// CREAT: 3
// CREAT: 3
// LINK: 0
// RENAME: 0
// RENAME: 0
// LINK: 0 (Success)

// ::Actual::
// CREAT: 3
// CREAT: 3
// LINK: 0
// RENAME: 0
// RENAME: 0
// LINK: -1 (File exists)

Steps:

  • Start glusterd service.
  • Create and mount 1 XFS filesystems on 1 device.
  • Create and start volume.
  • Mount volume.
  • Compile the script.
  • Change directory to volume root.
  • Run compiled script.
  • Observe that last operation fails.

Activity

  1. Slava0135 commented on Sep 24, 2026

    @Slava0135
    Author

    Here is the test that can be run using GlusterFS framework:

    $ ./run-tests.sh tests/bugs/posix/issue-xxxx.t
    
    ... GlusterFS Test Framework ...
    
    ~/glusterfs ~/glusterfs
    ~/glusterfs
    testing 'timeout' command
    
    ======================================== (565 / 848) ========================================
    [12:39:17] Running tests in file ./tests/bugs/posix/issue-xxxx.t
    ./tests/bugs/posix/issue-xxxx.t .. 
    1..9
    ok   1 [    111/   1229] <   9> 'glusterd'
    ok   2 [      9/     61] <  10> 'gluster --mode=script --wignore volume create patchy 10.0.2.15:/d/backends/patchy'
    ok   3 [     10/     84] <  11> 'gluster --mode=script --wignore volume start patchy'
    ok   4 [      9/     11] <  12> 'glusterfs -s 10.0.2.15 --volfile-id patchy /mnt/glusterfs/0'
    ok   5 [     13/     30] <  15> 'cc -Wall ./tests/bugs/posix/issue-xxxx.c -o ./tests/bugs/posix/issue-xxxx'
    link failed: File exists
    not ok   6 [     13/      4] <  21> './tests/bugs/posix/issue-xxxx /mnt/glusterfs/0/dir-issue-xxxx' -> ''
    ok   7 [      9/     11] <  24> 'Y force_umount /mnt/glusterfs/0'
    ok   8 [      9/   1073] <  25> 'gluster --mode=script --wignore volume stop patchy'
    ok   9 [      9/     63] <  26> 'gluster --mode=script --wignore volume delete patchy'
    Failed 1/9 subtests 
    
    Test Summary Report
    -------------------
    ./tests/bugs/posix/issue-xxxx.t (Wstat: 0 Tests: 9 Failed: 1)
      Failed test:  6
    Files=1, Tests=9,  3 wallclock secs ( 0.01 usr  0.00 sys +  0.33 cusr  0.28 csys =  0.62 CPU)
    Result: FAIL
    Logs preserved in tarball issue-xxxx-iteration-3.tar.gz
    ./tests/bugs/posix/issue-xxxx.t: bad status 1
    

    issue-xxxx.t:

    #!/bin/bash
    
    . $(dirname $0)/../../include.rc
    . $(dirname $0)/../../volume.rc
    
    cleanup;
    
    # Create, start and mount the volume.
    TEST glusterd;
    TEST $CLI volume create $V0 $H0:$B0/$V0;
    TEST $CLI volume start $V0;
    TEST glusterfs -s $H0 --volfile-id $V0 $M0
    
    # Compile the test program.
    TEST $CC -Wall $(dirname $0)/issue-xxxx.c -o $(dirname $0)/issue-xxxx
    
    # Create test directory.
    mkdir -p $M0/dir-issue-xxxx
    
    # Invoke the test program and pass path of directory to it.
    TEST $(dirname $0)/issue-xxxx $M0/dir-issue-xxxx
    
    # Unmount, stop and delete the volume
    EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
    TEST $CLI volume stop $V0;
    TEST $CLI volume delete $V0;
    
    cleanup;

    issue-xxxx.c:

    #include <assert.h>
    #include <dirent.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(int argc, char **argv) {
      char *path = NULL;
    
      assert(argc == 2);
      path = argv[1];
    
      int status;
    
      status = chdir(path);
    
      status = creat("file1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
      if (status < 0) {
        fprintf(stderr, "creat failed: %s\n", strerror(errno));
        return -1;
      }
      close(status);
    
      status = creat("file2", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
      if (status < 0) {
        fprintf(stderr, "creat failed: %s\n", strerror(errno));
        return -1;
      }
      close(status);
    
      status = link("file2", "ln1");
      if (status < 0) {
        fprintf(stderr, "link failed: %s\n", strerror(errno));
        return -1;
      }
    
      status = rename("file1", "ln1");
      if (status < 0) {
        fprintf(stderr, "rename failed: %s\n", strerror(errno));
        return -1;
      }
    
      status = rename("ln1", "ln2");
      if (status < 0) {
        fprintf(stderr, "rename failed: %s\n", strerror(errno));
        return -1;
      }
    
      status = link("file2", "ln1");
      if (status < 0) {
        fprintf(stderr, "link failed: %s\n", strerror(errno));
        return -1;
      }
    
      return 0;
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions