test: avoid false failure with setgid directories
[coreutils.git] / src / force-link.c
blob543f6966b9307e42b81a07acf5a3f98559acf25b
1 /* Implement ln -f "atomically"
3 Copyright 2017-2024 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 /* Written by Paul Eggert. */
20 /* A naive "ln -f A B" unlinks B and then links A to B. This module
21 instead links A to a randomly-named temporary T in B's directory,
22 and then renames T to B. This approach has a window with a
23 randomly-named temporary, which is safer for many applications than
24 a window where B does not exist. */
26 #include <config.h>
27 #include "system.h"
29 #include "force-link.h"
31 #include <tempname.h>
33 /* A basename pattern suitable for a temporary file. It should work
34 even on file systems like FAT that support only short names.
35 "Cu" is short for "Coreutils" or for "Changeable unstable",
36 take your pick.... */
38 static char const simple_pattern[] = "CuXXXXXX";
39 enum { x_suffix_len = sizeof "XXXXXX" - 1 };
41 /* A size for smallish buffers containing file names. Longer file
42 names can use malloc. */
44 enum { smallsize = 256 };
46 /* Return a template for a file in the same directory as DSTNAME.
47 Use BUF if the template fits, otherwise use malloc and return nullptr
48 (setting errno) if unsuccessful. */
50 static char *
51 samedir_template (char const *dstname, char buf[smallsize])
53 ptrdiff_t dstdirlen = last_component (dstname) - dstname;
54 size_t dsttmpsize = dstdirlen + sizeof simple_pattern;
55 char *dsttmp;
56 if (dsttmpsize <= smallsize)
57 dsttmp = buf;
58 else
60 dsttmp = malloc (dsttmpsize);
61 if (!dsttmp)
62 return dsttmp;
64 strcpy (mempcpy (dsttmp, dstname, dstdirlen), simple_pattern);
65 return dsttmp;
69 /* Auxiliaries for force_linkat. */
71 struct link_arg
73 int srcdir;
74 char const *srcname;
75 int dstdir;
76 int flags;
79 static int
80 try_link (char *dest, void *arg)
82 struct link_arg *a = arg;
83 return linkat (a->srcdir, a->srcname, a->dstdir, dest, a->flags);
86 /* Hard-link directory SRCDIR's file SRCNAME to directory DSTDIR's
87 file DSTNAME, using linkat-style FLAGS to control the linking.
88 If FORCE and DSTNAME already exists, replace it atomically.
89 If LINKAT_ERRNO is 0, the hard link is already done; if positive,
90 the hard link was tried and failed with errno == LINKAT_ERRNO. Return
91 -1 if successful and DSTNAME already existed,
92 0 if successful and DSTNAME did not already exist, and
93 a positive errno value on failure. */
94 extern int
95 force_linkat (int srcdir, char const *srcname,
96 int dstdir, char const *dstname, int flags, bool force,
97 int linkat_errno)
99 if (linkat_errno < 0)
100 linkat_errno = (linkat (srcdir, srcname, dstdir, dstname, flags) == 0
101 ? 0 : errno);
102 if (!force || linkat_errno != EEXIST)
103 return linkat_errno;
105 char buf[smallsize];
106 char *dsttmp = samedir_template (dstname, buf);
107 if (! dsttmp)
108 return errno;
109 struct link_arg arg = { srcdir, srcname, dstdir, flags };
110 int err;
112 if (try_tempname_len (dsttmp, 0, &arg, try_link, x_suffix_len) != 0)
113 err = errno;
114 else
116 err = renameat (dstdir, dsttmp, dstdir, dstname) == 0 ? -1 : errno;
117 /* Unlink DSTTMP even if renameat succeeded, in case DSTTMP
118 and DSTNAME were already the same hard link and renameat
119 was a no-op. */
120 unlinkat (dstdir, dsttmp, 0);
123 if (dsttmp != buf)
124 free (dsttmp);
125 return err;
129 /* Auxiliaries for force_symlinkat. */
131 struct symlink_arg
133 char const *srcname;
134 int dstdir;
137 static int
138 try_symlink (char *dest, void *arg)
140 struct symlink_arg *a = arg;
141 return symlinkat (a->srcname, a->dstdir, dest);
144 /* Create a symlink containing SRCNAME in directory DSTDIR's file DSTNAME.
145 If FORCE and DSTNAME already exists, replace it atomically.
146 If SYMLINKAT_ERRNO is 0, the symlink is already done; if positive,
147 the symlink was tried and failed with errno == SYMLINKAT_ERRNO. Return
148 -1 if successful and DSTNAME already existed,
149 0 if successful and DSTNAME did not already exist, and
150 a positive errno value on failure. */
151 extern int
152 force_symlinkat (char const *srcname, int dstdir, char const *dstname,
153 bool force, int symlinkat_errno)
155 if (symlinkat_errno < 0)
156 symlinkat_errno = symlinkat (srcname, dstdir, dstname) == 0 ? 0 : errno;
157 if (!force || symlinkat_errno != EEXIST)
158 return symlinkat_errno;
160 char buf[smallsize];
161 char *dsttmp = samedir_template (dstname, buf);
162 if (!dsttmp)
163 return errno;
164 struct symlink_arg arg = { srcname, dstdir };
165 int err;
167 if (try_tempname_len (dsttmp, 0, &arg, try_symlink, x_suffix_len) != 0)
168 err = errno;
169 else if (renameat (dstdir, dsttmp, dstdir, dstname) != 0)
171 err = errno;
172 unlinkat (dstdir, dsttmp, 0);
174 else
176 /* Don't worry about renameat being a no-op, since DSTTMP is
177 newly created. */
178 err = -1;
181 if (dsttmp != buf)
182 free (dsttmp);
183 return err;