1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
/*
* Copyright (c) 2012, 2013 Wind River Systems; see
* guts/COPYRIGHT for information.
*
* int linkat(int olddirfd, const char *oldname, int newdirfd, const char *newname, int flags)
* int rc = -1;
*/
int rc2, rflags, save_errno;
pseudo_msg_t *msg;
char *oldpath = NULL, *newpath = NULL;
PSEUDO_STATBUF buf;
/* This is gratuitously complicated. On Linux 2.6.18 and later,
* flags may contain AT_SYMLINK_FOLLOW, which implies following
* symlinks; otherwise, linkat() will *not* follow symlinks. FreeBSD
* appears to use the same semantics.
*
* So on Darwin, always pass AT_SYMLINK_FOLLOW, because the
* alternative doesn't work. And never pass AT_SYMLINK_NOFOLLOW
* because that's not a valid flag to linkat().
*
* So we need a flag for path resolution which is AT_SYMLINK_NOFOLLOW
* unless AT_SYMLINK_FOLLOW was specified, in which case it's 0.
*/
rflags = (flags & AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) {
errno = ENOSYS;
return -1;
}
#endif
oldpath = pseudo_root_path(__func__, __LINE__, olddirfd, oldname, rflags);
newpath = pseudo_root_path(__func__, __LINE__, newdirfd, newname, AT_SYMLINK_NOFOLLOW);
rc = real_link(oldpath, newpath);
save_errno = errno;
if (rc == -1) {
free(oldpath);
free(newpath);
errno = save_errno;
return rc;
}
/* if we got this far, the link succeeded, and oldpath and newpath
* are the newly-allocated canonical paths. If OS, filesystem, or
* the flags value prevent hard linking to symlinks, the resolved
* path should be the target's path anyway, so lstat is safe here.
*/
/* find the target: */
rc2 = base_lstat(oldpath, &buf);
if (rc2 == -1) {
pseudo_diag("Fatal: Tried to stat '%s' after linking it, but failed: %s.\n",
oldpath, strerror(errno));
free(oldpath);
free(newpath);
errno = ENOENT;
return rc;
}
msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, &buf);
if (msg) {
pseudo_stat_msg(&buf, msg);
}
/* Long story short: I am pretty sure we still want OP_LINK even
* if the thing linked is a symlink.
*/
pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &buf);
free(oldpath);
free(newpath);
errno = save_errno;
/* return rc;
* }
*/
|