MCPcopy Create free account
hub / github.com/RsyncProject/rsync / do_link_at

Function do_link_at

syscall.c:315–399  ·  view source on GitHub ↗

Symlink-race-safe variant of do_link() for receiver-side use. See the comment on do_chmod_at() for the threat model. link() resolves parent components of *both* old_path and new_path, so a parent- symlink swap on either side can plant the new hard link outside the module, or hard-link an outside file into the module (read disclosure). Defence: open each parent under secure_relative_op

Source from the content-addressed store, hash-verified

313 pre-AT_FDCWD systems fall through to do_link().
314*/
315int do_link_at(const char *old_path, const char *new_path)
316{
317#if defined AT_FDCWD && defined HAVE_LINKAT
318 extern int am_daemon, am_chrooted;
319 char old_dirpath[MAXPATHLEN], new_dirpath[MAXPATHLEN];
320 const char *old_bname, *new_bname;
321 const char *old_slash, *new_slash;
322 int old_dfd = AT_FDCWD, new_dfd = AT_FDCWD;
323 BOOL old_owns = False, new_owns = False;
324 int ret, e;
325 size_t old_dlen = 0, new_dlen = 0;
326
327 if (dry_run) return 0;
328 RETURN_ERROR_IF_RO_OR_LO;
329
330 if (!am_daemon || am_chrooted)
331 return do_link(old_path, new_path);
332
333 if (!old_path || !*old_path || *old_path == '/'
334 || !new_path || !*new_path || *new_path == '/')
335 return do_link(old_path, new_path);
336
337 old_slash = strrchr(old_path, '/');
338 new_slash = strrchr(new_path, '/');
339
340 /* Resolve each path's parent dir independently. A path without a
341 * slash lives in CWD (AT_FDCWD), no parent open required. A path
342 * with a slash needs secure_relative_open to confine its parent
343 * resolution -- otherwise a parent symlink (e.g. --link-dest=cd
344 * where cd -> /outside) lets the kernel-level linkat(AT_FDCWD,
345 * "cd/target.txt", ...) escape the module. */
346 if (old_slash) {
347 old_dlen = old_slash - old_path;
348 if (old_dlen >= sizeof old_dirpath) { errno = ENAMETOOLONG; return -1; }
349 memcpy(old_dirpath, old_path, old_dlen);
350 old_dirpath[old_dlen] = '\0';
351 old_bname = old_slash + 1;
352 old_dfd = secure_relative_open(NULL, old_dirpath, O_RDONLY | O_DIRECTORY, 0);
353 if (old_dfd < 0)
354 return -1;
355 old_owns = True;
356 } else {
357 old_bname = old_path;
358 }
359
360 if (new_slash) {
361 new_dlen = new_slash - new_path;
362 if (new_dlen >= sizeof new_dirpath) {
363 e = ENAMETOOLONG;
364 if (old_owns) close(old_dfd);
365 errno = e;
366 return -1;
367 }
368 memcpy(new_dirpath, new_path, new_dlen);
369 new_dirpath[new_dlen] = '\0';
370 new_bname = new_slash + 1;
371 if (old_owns && old_dlen == new_dlen
372 && memcmp(old_dirpath, new_dirpath, old_dlen) == 0) {

Callers 3

hard_link_oneFunction · 0.85
link_or_renameFunction · 0.85
try_dests_nonFunction · 0.85

Calls 2

do_linkFunction · 0.85
secure_relative_openFunction · 0.85

Tested by

no test coverage detected