From: Waldemar Kozaczuk <[email protected]>
Committer: Waldemar Kozaczuk <[email protected]>
Branch: master
vfs: make sys_utimensat handle AT_SYMLINK_NOFOLLOW
This patch adds missing logic (marked by TODO) to handle AT_SYMLINK_NOFOLLOW
in the implementation of sys_utimensat().
We do it by extracting relevant code from sys_utimes() into
new helper function handle_symlink_nofollow() which is then reused
in both sys_utimes() and sys_utimensat().
Please note the tst-utimensat.cc has to be slightly adjusted with
ifdef to account for differences between how gibc and OSv libc handles
this case with utimensat():
"Force utimensat to fail when dirfd was AT_FDCWD and pathname is NULL"
Linux manual states this about when EFAULT errno is set:
"EFAULT times pointed to an invalid address; or, dirfd was
AT_FDCWD, and pathname is NULL or an invalid address.
Likewise, we also have to adjust tst-utimes.cc with ifdef to account
for differences between how utimes() called with invalid times argument
gets handled by glibc and OSv libc.
Changes in this patch and 8 previous patches make it possible
to run more unit tests on OSv with linux dynamic linker:
scripts/test.py --linux_ld -m modules/tests-with-linux-ld/usr.manifest \
-d tst-kill \
-d tst-pthread-clock \
-d tst-sigaction \
-d tst-sigwait \
-d tst-stdio-rofs \
-d tst-wctype
Signed-off-by: Waldemar Kozaczuk <[email protected]>
---
diff --git a/fs/vfs/vfs_syscalls.cc b/fs/vfs/vfs_syscalls.cc
--- a/fs/vfs/vfs_syscalls.cc
+++ b/fs/vfs/vfs_syscalls.cc
@@ -1248,41 +1248,52 @@ static void convert_timeval(struct timespec &to, const
struct timeval *from)
}
}
-int
-sys_utimes(char *path, const struct timeval times[2], int flags)
+static int handle_symlink_nofollow(const char *path, int flags, struct dentry
**dpp)
{
int error;
- struct dentry *dp;
- struct timespec timespec_times[2];
-
- DPRINTF(VFSDB_SYSCALL, ("sys_utimes: path=%s\n", path));
-
- if (times && (!is_timeval_valid(×[0]) ||
!is_timeval_valid(×[1])))
- return EINVAL;
-
- // Convert each element of timeval array to the timespec type
- convert_timeval(timespec_times[0], times ? times + 0 : nullptr);
- convert_timeval(timespec_times[1], times ? times + 1 : nullptr);
-
+ char *_path = const_cast<char *>(path);
if (flags & AT_SYMLINK_NOFOLLOW) {
struct dentry *ddp;
- error = lookup(path, &ddp, nullptr);
+ error = lookup(_path, &ddp, nullptr);
if (error) {
return error;
}
- error = namei_last_nofollow(path, ddp, &dp);
+ error = namei_last_nofollow(_path, ddp, dpp);
if (ddp != nullptr) {
drele(ddp);
}
if (error) {
return error;
}
} else {
- error = namei(path, &dp);
+ error = namei(_path, dpp);
if (error)
return error;
}
+ return 0;
+}
+
+int
+sys_utimes(char *path, const struct timeval times[2], int flags)
+{
+ int error;
+ struct dentry *dp;
+ struct timespec timespec_times[2];
+
+ DPRINTF(VFSDB_SYSCALL, ("sys_utimes: path=%s\n", path));
+
+ if (times && (!is_timeval_valid(×[0]) ||
!is_timeval_valid(×[1])))
+ return EINVAL;
+
+ // Convert each element of timeval array to the timespec type
+ convert_timeval(timespec_times[0], times ? times + 0 : nullptr);
+ convert_timeval(timespec_times[1], times ? times + 1 : nullptr);
+
+ error = handle_symlink_nofollow(path, flags, &dp);
+ if (error) {
+ return error;
+ }
if (dp->d_mount->m_flags & MNT_RDONLY) {
error = EROFS;
@@ -1367,12 +1378,10 @@ sys_utimensat(int dirfd, const char *pathname, const
struct timespec times[2], i
ap = std::string(fp->f_dentry->d_mount->m_path) + "/" + ap;
}
- /* FIXME: Add support for AT_SYMLINK_NOFOLLOW */
-
- error = namei(ap.c_str(), &dp);
-
- if (error)
+ error = handle_symlink_nofollow(ap.c_str(), flags, &dp);
+ if (error) {
return error;
+ }
if (dp->d_mount->m_flags & MNT_RDONLY) {
error = EROFS;
diff --git a/tests/tst-utimensat.cc b/tests/tst-utimensat.cc
--- a/tests/tst-utimensat.cc
+++ b/tests/tst-utimensat.cc
@@ -100,9 +100,9 @@ int main(int argc, char *argv[])
ret = utimensat(AT_FDCWD, rel_path_bar_to_tmp, times, 0);
report(ret == 0, "utimensat worked successfully with AT_FDCWD");
- /* Use dirfd and relative path of bar to check utimensat */
- ret = utimensat(dirfd, rel_path_bar_to_foo, times, 0);
- report(ret == 0, "utimensat works with dirfd and relative path");
+ /* Use dirfd and relative path of bar to check utimensat */
+ ret = utimensat(dirfd, rel_path_bar_to_foo, times, 0);
+ report(ret == 0, "utimensat works with dirfd and relative path");
/* Force utimensat to fail using invalid dirfd */
ret = utimensat(100, rel_path_bar_to_foo, times, 0);
@@ -114,15 +114,19 @@ int main(int argc, char *argv[])
/* Force utimensat to fail when dirfd was AT_FDCWD and pathname is NULL */
ret = utimensat(AT_FDCWD, NULL, times, 0);
+#ifdef LINUX
+ report(ret == -1 && errno == EINVAL, "utimensat fails when dirfd is
AT_FDCWD and pathname is NULL");
+#else
report(ret == -1 && errno == EFAULT, "utimensat fails when dirfd is
AT_FDCWD and pathname is NULL");
+#endif
/* Force utimensat to fail with invalid flags */
ret = utimensat(dirfd, rel_path_bar_to_tmp, times, 23);
report(ret == -1 && errno == EINVAL, "utimensat fails with invalid flags");
/* Force utimensat to fail with invalid values in times */
- init_timespec(times[0], -1, 100); /* change atime */
- ret = utimensat(dirfd, rel_path_bar_to_tmp, times, 0);
+ init_timespec(times[0], -1, -1); /* change atime */
+ ret = utimensat(dirfd, rel_path_bar_to_foo, times, 0);
report(ret == -1 && errno == EINVAL, "utimensat fails with invalid value
in times");
init_timespec(times[0], 1234, 100);
@@ -143,8 +147,8 @@ int main(int argc, char *argv[])
report(ret == -1 && errno == ENOTDIR, "utimensat fails when dirfd points
to file and relative pathname is given");
/* Clean up temporary files */
- report(unlink(tmp_file_bar) == 0, "remove the file bar");
- report(rmdir(tmp_folder_foo) == 0, "remove the folder foo");
+ report(unlink(tmp_file_bar) == 0, "remove the file bar");
+ report(rmdir(tmp_folder_foo) == 0, "remove the folder foo");
/* Report results. */
printf("SUMMARY: %d tests, %d failures\n", tests, fails);
diff --git a/tests/tst-utimes.cc b/tests/tst-utimes.cc
--- a/tests/tst-utimes.cc
+++ b/tests/tst-utimes.cc
@@ -82,7 +82,7 @@ int main(int argc, char *argv[])
report(stat(path, &st) == 0, "stat the file");
/* Dump current atime and mtime */
- printf("ino: %u - old -> st_atim: %ld:%ld - st_mtim: %ld:%ld\n",
+ printf("ino: %lu - old -> st_atim: %ld:%ld - st_mtim: %ld:%ld\n",
st.st_ino,
st.st_atim.tv_sec, st.st_atim.tv_nsec,
st.st_mtim.tv_sec, st.st_mtim.tv_nsec);
@@ -110,7 +110,7 @@ int main(int argc, char *argv[])
"check changes made to mtime");
/* Dump the changes to atime and mtime */
- printf("ino: %u - new -> st_atim: %ld:%ld - st_mtim: %ld:%ld\n",
+ printf("ino: %lu - new -> st_atim: %ld:%ld - st_mtim: %ld:%ld\n",
st.st_ino,
st.st_atim.tv_sec, st.st_atim.tv_nsec,
st.st_mtim.tv_sec, st.st_mtim.tv_nsec);
@@ -138,11 +138,13 @@ int main(int argc, char *argv[])
"check if utimes made changes to link target's mtime");
+#ifndef LINUX
/* Force utimes to fail */
times[0].tv_sec = -1;
ret = utimes(path, times);
report(ret == -1 && errno == EINVAL,
"check if utimes failed as desired!");
+#endif
/* Check if utimes works with NULL as argument */
ret = utimes(path, NULL);
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/osv-dev/000000000000b8af27060c90822d%40google.com.