summaryrefslogtreecommitdiff
path: root/syscall.c
diff options
context:
space:
mode:
authorWayne Davison <wayned@samba.org>2011-09-19 09:18:18 -0700
committerWayne Davison <wayned@samba.org>2011-09-19 09:29:00 -0700
commit953feeadd2d02e646edd2a35e3cc8465335a09dc (patch)
tree8fcb41ef5abde526d31a43dacd9e194d79617d7d /syscall.c
parent3417881d5cf5358faa9484fb487775f0e8b14556 (diff)
downloadrsync-953feeadd2d02e646edd2a35e3cc8465335a09dc.tar.gz
Make do_readlink() support fake-super w/o O_NOFOLLOW.
Diffstat (limited to 'syscall.c')
-rw-r--r--syscall.c49
1 files changed, 48 insertions, 1 deletions
diff --git a/syscall.c b/syscall.c
index c6c571a6..6d51d3cb 100644
--- a/syscall.c
+++ b/syscall.c
@@ -87,7 +87,7 @@ ssize_t do_readlink(const char *path, char *buf, size_t bufsiz)
{
/* For --fake-super, we read the link from the file. */
if (am_root < 0) {
- int fd = open(path, O_RDONLY|O_NOFOLLOW);
+ int fd = do_open_nofollow(path, O_RDONLY);
if (fd >= 0) {
int len = read(fd, buf, bufsiz);
close(fd);
@@ -443,3 +443,50 @@ int do_fallocate(int fd, OFF_T offset, OFF_T length)
#endif
}
#endif
+
+int do_open_nofollow(const char *pathname, int flags)
+{
+#ifndef O_NOFOLLOW
+ struct stat f_st, l_st;
+#endif
+ int fd;
+
+ if (flags != O_RDONLY) {
+ RETURN_ERROR_IF(dry_run, 0);
+ RETURN_ERROR_IF_RO_OR_LO;
+#ifndef O_NOFOLLOW
+ /* This function doesn't support write attempts w/o O_NOFOLLOW. */
+ errno = EINVAL;
+ return -1;
+#endif
+ }
+
+#ifdef O_NOFOLLOW
+ fd = open(pathname, flags|O_NOFOLLOW);
+#else
+ if ((fd = open(pathname, flags)) < 0)
+ return fd;
+
+ if (do_fstat(fd, &f_st) < 0) {
+ close_and_return_error:
+ {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ }
+ return -1;
+ }
+ if (do_lstat(pathname, &l_st) < 0)
+ goto close_and_return_error;
+ if (S_ISLNK(l_st.st_mode)) {
+ errno = ELOOP;
+ goto close_and_return_error;
+ }
+ if (l_st.st_dev != f_st.st_dev || l_st.st_ino != f_st.st_ino) {
+ errno = EINVAL;
+ goto close_and_return_error;
+ }
+#endif
+
+ return fd;
+}