Create a symlink containing SRCNAME in directory DSTDIR's file DSTNAME. If FORCE and DSTNAME already exists, replace it atomically. If SYMLINKAT_ERRNO is 0, the symlink is already done; if positive, the symlink was tried and failed with errno == SYMLINKAT_ERRNO. Return -1 if successful and DSTNAME already existed, 0 if successful and DSTNAME did not already exist, and a positive
| 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) |
| 154 | { |
| 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; |
| 159 | |
| 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; |
| 166 | |
| 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) |
| 170 | { |
| 171 | err = errno; |
| 172 | unlinkat (dstdir, dsttmp, 0); |
| 173 | } |
| 174 | else |
| 175 | { |
| 176 | /* Don't worry about renameat being a no-op, since DSTTMP is |
| 177 | newly created. */ |
| 178 | err = -1; |
| 179 | } |
| 180 | |
| 181 | if (dsttmp != buf) |
| 182 | free (dsttmp); |
| 183 | return err; |
| 184 | } |
no test coverage detected