Hello,
I am trying to implement utimensat() and futimens() functions and need a bit of help
in setting the timespec arguments.
1. How do we get the current time for UTIME_NOW?
2. How do we deal with UTIME_OMIT? I could think of two ways, using vn_stat(heavy) or modifying
vn_settimes() to accept which attrs to modify ( ATIME or MTIME or both ).
Also, man page of utimes() states that it should work with NULL as argument. This does
not happen in the present implementation and the tst-utimes also does not cover it.
On Thu, Jun 12, 2014 at 12:59 PM, Jaspal Singh Dhillon <jaspal...@gmail.com> wrote:
Hello,
I am trying to implement utimensat() and futimens() functions and need a bit of help
in setting the timespec arguments.
1. How do we get the current time for UTIME_NOW?
Probably the easiest is to use the POSIX API, clock_gettime(), which returns the time already in timespec format.
Alternatively, you can use OSv's C++11-based time API, described in https://github.com/cloudius-systems/osv/wiki/Telling-the-time. You can also see an example of how to use that OSv API in our implementation of clock_gettime(), which is pretty trivial.
2. How do we deal with UTIME_OMIT? I could think of two ways, using vn_stat(heavy) or modifying
vn_settimes() to accept which attrs to modify ( ATIME or MTIME or both ).
I think the best and easiest would be to modify vn_settimes() to do something like this:
vap->va_mask = ((times[0].tv_nsec == UTIME_OMIT) ? AT_ATIME : 0)
| ((times[1].tv_nsec == UTIME_OMIT) ? AT_MTIME : 0)
That is, unless we make sure all the VOP_SETATTR implementations already check for UTIME_OMIT? Can you please look perhaps they do?
I don't think that the only implementation at present for VOP_SETATTR in zfs_vnops.c , zfs_setattr() checks for UTIME_OMIT anywhere in its implementation because besides being defined in stat.h, it is not used/checked anywhere!
vap->va_mask = ((times[0].tv_nsec == UTIME_OMIT) ? AT_ATIME : 0)
| ((times[1].tv_nsec == UTIME_OMIT) ? AT_MTIME : 0)
That is, unless we make sure all the VOP_SETATTR implementations already check for UTIME_OMIT? Can you please look perhaps they do?
+}
+
+void init_timespec(struct timespec &_times, const struct timespec times)
+{
+
+ if (times.tv_nsec == UTIME_NOW) {
+ clock_gettime(CLOCK_REALTIME, &_times);
+ } else if (times.tv_nsec != UTIME_OMIT) {
+ _times.tv_sec = times.tv_sec;
+ _times.tv_nsec = times.tv_nsec;
+ }
+ return;
+}
+
+int
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags)
+{
+ int error;
+ struct dentry *dp;
+ char *absolute_path;
+ struct file *fp;
+ int length;
+ struct timespec timespec_times[2];
+ extern struct task *main_task;
+
+ if (times && (!is_timespec_valid(×[0]) || !is_timespec_valid(×[1])))
+ return EINVAL;
+
+ if (times) {
+ init_timespec(timespec_times[0],times[0]);
+ init_timespec(timespec_times[1],times[1]);
+ } else {
+ clock_gettime(CLOCK_REALTIME, ×pec_times[0]);
+ clock_gettime(CLOCK_REALTIME, ×pec_times[1]);
+ }
+
+ /* utimensat should return ENOENT when pathname is empty */
+ if(pathname && pathname[0] == 0)
+ return ENOENT;
+
+ if (pathname && pathname[0] == '/') {
+ absolute_path = (char*)malloc(strlen(pathname));
+ strcpy(absolute_path, pathname);
+ } else if (dirfd == AT_FDCWD) {
+ if (!pathname)
+ return EFAULT;
+ length = strlen(main_task->t_cwd) + strlen(pathname) + 2;
+ absolute_path = (char*)malloc(length);
+ snprintf(absolute_path, length, "%s/%s", main_task->t_cwd, pathname);
+ } else {
+ error = fget(dirfd, &fp);
+ if (error)
+ return error;
+
+ if(!fp->f_dentry)
+ return EBADF;
+
+ if (!(fp->f_dentry->d_vnode->v_type & VDIR)){
+ return ENOTDIR;
+ }
+
+ length = strlen(fp->f_dentry->d_path) + strlen(pathname) + 2;
+ absolute_path = (char*)malloc(length);
+
+ if (pathname)
+ snprintf(absolute_path, length, "%s/%s", fp->f_dentry->d_path, pathname);
+ else
+ strcpy(absolute_path, pathname);
+ fdrop(fp);
+ }
+ /* FIXME: Add support to follow symbolic links */
+ if (flags && !(flags & AT_SYMLINK_NOFOLLOW))
+ return EINVAL;
+
+ error = namei(absolute_path, &dp);
--
1.9.0
--
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 osv-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
+}
+
+void init_timespec(struct timespec &_times, const struct timespec times)
+{
+
+ if (times.tv_nsec == UTIME_NOW) {
+ clock_gettime(CLOCK_REALTIME, &_times);
+ } else if (times.tv_nsec != UTIME_OMIT) {
+ _times.tv_sec = times.tv_sec;
+ _times.tv_nsec = times.tv_nsec;
Why the if (times.tv_nsec != UTIME_OMIT) at all, and not a straight "else"?
Even if it's UTIME_OMIT, you need to copy it, no?
+ }
+ return;
+}
+
+int
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags)
+{
+ int error;
+ struct dentry *dp;
+ char *absolute_path;
+ struct file *fp;
+ int length;
+ struct timespec timespec_times[2];
+ extern struct task *main_task;
Nitpick: in C++ (and also post-diluvian C) , you don't need to stuff all these declarations in the top of the function. You can put them closer to where you need them, preferably with the value.
+
+ if (times && (!is_timespec_valid(×[0]) || !is_timespec_valid(×[1])))
+ return EINVAL;
+
+ if (times) {
+ init_timespec(timespec_times[0],times[0]);
+ init_timespec(timespec_times[1],times[1]);
Please use a space after the commas.
+ } else {
+ clock_gettime(CLOCK_REALTIME, ×pec_times[0]);
+ clock_gettime(CLOCK_REALTIME, ×pec_times[1]);
Nitpick: (feel free to reject)
You're repeating here the clock_gettime() code, which I guess is ok, but doesn't look very pretty to me. One alternative is to make init_timespec() take a pointer, not a reference (yes, I realize how ironic this suggestion is considering my earlier comment...), and if this point is 0 also call clock_gettime. Then your call here becomes something like
init_timespec(timespec_times[0], times ? times+0 : nullptr);
+ }
+
+ /* utimensat should return ENOENT when pathname is empty */
+ if(pathname && pathname[0] == 0)
+ return ENOENT;
+
+ if (pathname && pathname[0] == '/') {
+ absolute_path = (char*)malloc(strlen(pathname));
+ strcpy(absolute_path, pathname);
+ } else if (dirfd == AT_FDCWD) {
+ if (!pathname)
+ return EFAULT;
+ length = strlen(main_task->t_cwd) + strlen(pathname) + 2;
+ absolute_path = (char*)malloc(length);
Please use unique_ptr to avoid leaking this memory. Already I see below one case you return() without freeing this.
+ snprintf(absolute_path, length, "%s/%s", main_task->t_cwd, pathname);
If main_task->t_cwd changed between the time we measured its length and copy it here, we're toast.
But to be honest, I don't understand how t_cwd locking happens at all...
(in any case, I am guessing there is a better way to do this function, without manipulating absolute paths... but maybe we can do that later...).
+ } else {
+ error = fget(dirfd, &fp);
+ if (error)
+ return error;
+
+ if(!fp->f_dentry)
+ return EBADF;
+
+ if (!(fp->f_dentry->d_vnode->v_type & VDIR)){
+ return ENOTDIR;
+ }
+
+ length = strlen(fp->f_dentry->d_path) + strlen(pathname) + 2;
You need to put this in the if(pathname) below, otherwise strlen() will crash.
+ absolute_path = (char*)malloc(length);
+
+ if (pathname)
+ snprintf(absolute_path, length, "%s/%s", fp->f_dentry->d_path, pathname);
+ else
+ strcpy(absolute_path, pathname);
+ fdrop(fp);
+ }
+ /* FIXME: Add support to follow symbolic links */
+ if (flags && !(flags & AT_SYMLINK_NOFOLLOW))
+ return EINVAL;
+
+ error = namei(absolute_path, &dp);
Do we have a namei() alternative taking a dir and a relative path, so you won't have to manipulate the absolute paths as above?
+ if (error) {
+ trace_vfs_utimensat_err(error);
+ errno = error;
+ return -1;
+ }
+
+ trace_vfs_utimensat_ret();
+ return 0;
{
+ clock_gettime(CLOCK_REALTIME, &_times);
+ } else if (times->tv_nsec != UTIME_OMIT)
+ _times.tv_sec = times->tv_sec;
+ _times.tv_nsec = times->tv_nsec;
+ }
+ return;
+}
+
+int
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags)
+{
+ int error;
+ std::unique_ptr<std::string> ap (new std::string());
+ struct timespec timespec_times[2];
+ extern struct task *main_task;
+ struct dentry *dp;
+
+ if (times && (!is_timespec_valid(times[0]) || !is_timespec_valid(times[1])))
+ return EINVAL;
+
+ init_timespec(timespec_times[0], times ? times + 0 : nullptr);
+ init_timespec(timespec_times[1], times ? times + 1 : nullptr);
+
+ /* utimensat should return ENOENT when pathname is empty */
+ if(pathname && pathname[0] == 0)
+ return ENOENT;
+
+ if (pathname && pathname[0] == '/') {
+ *ap = std::string(pathname);
+ } else if (dirfd == AT_FDCWD) {
+ if (!pathname)
+ return EFAULT;
+ *ap = std::string(main_task->t_cwd) + std::string("/") + std::string(pathname);
+ } else {
+ struct file *fp;
+
+ error = fget(dirfd, &fp);
+ if (error)
+ return error;
+
+ if(!fp->f_dentry)
+ return EBADF;
++
+ if (!(fp->f_dentry->d_vnode->v_type & VDIR))
+ return ENOTDIR;
+
+ if (pathname)
+ *ap = std::string(fp->f_dentry->d_path) + std::string("/") + std::string(pathname);
+ else
+ *ap = std::string(fp->f_dentry->d_path);
+ fdrop(fp);
+ }
+ /* FIXME: Add support for AT_SYMLINK_NOFOLLOW */
+ if (flags && !(flags & AT_SYMLINK_NOFOLLOW))
+ return EINVAL;
+
+ auto absolute_path = new char[ap->length()+1];
+ strcpy(absolute_path, ap->c_str());
+ error = sys_utimensat(AT_FDCWD, pathname, times, 0);
+ free(pathname);
+ return error;
+}
+
int
sys_fallocate(struct file *fp, int mode, loff_t offset, loff_t len)
{
diff --git a/fs/vfs/vfs_vnode.c b/fs/vfs/vfs_vnode.c
index cbed3fc..98445f4 100644
--- a/fs/vfs/vfs_vnode.c
+++ b/fs/vfs/vfs_vnode.c
@@ -400,8 +400,8 @@ vn_settimes(struct vnode *vp, struct timespec times[2])
vap->va_atime = times[0];
vap->va_mtime = times[1];
- vap->va_mask = AT_ATIME | AT_MTIME;
-
+ vap->va_mask = ((times[0].tv_nsec == UTIME_OMIT) ? 0 : AT_ATIME)
+ | ((times[1].tv_nsec == UTIME_OMIT) ? 0 : AT_MTIME);
vn_lock(vp);
error = VOP_SETATTR(vp, vap);
vn_unlock(vp);
--
1.9.0
--
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 osv-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
{
+ clock_gettime(CLOCK_REALTIME, &_times);
+ } else if (times->tv_nsec != UTIME_OMIT)
+ _times.tv_sec = times->tv_sec;
+ _times.tv_nsec = times->tv_nsec;
I still don't understand why you need to check nsec != UTIME_OMIT here. What harm can happen if you copy the timespec even in the UTIME_OMIT case? See below more about this issue.
+ }
+ return;
+}
+
+int
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags)
+{
+ int error;
+ std::unique_ptr<std::string> ap (new std::string());
Unless I'm misunderstanding something, you don't need all this complexity - a simple
std::string ap;
will do, and work just as well, and all the code below will be less ugly (e.g.,
instead of *ap = std::string(pathname), you can just do ap = pathname;
For
*ap = std::string(main_task->t_cwd) + std::string("/") + std::string(pathname);
you may still need to do
ap = std::string(main_task->t_cwd) + "/" + pathname;
(I think the first std::string is needed because otherwise, C++ will not even know std::strings are involved.
BTW, all of this string processing is likely to be less efficient than the explicit strlen & malloc code you had earlier, but I doubt this matters for this function, and I agree this code is nicer)
+ struct timespec timespec_times[2];
+ extern struct task *main_task;
+ struct dentry *dp;
+
+ if (times && (!is_timespec_valid(times[0]) || !is_timespec_valid(times[1])))
+ return EINVAL;
+
+ init_timespec(timespec_times[0], times ? times + 0 : nullptr);
+ init_timespec(timespec_times[1], times ? times + 1 : nullptr);
+
+ /* utimensat should return ENOENT when pathname is empty */
+ if(pathname && pathname[0] == 0)
+ return ENOENT;
Nitpick: It makes sense (though obviously not important) to do this sort of parameter checking before the init_timespec calls.
+
+ if (pathname && pathname[0] == '/') {
+ *ap = std::string(pathname);
+ } else if (dirfd == AT_FDCWD) {
+ if (!pathname)
+ return EFAULT;
+ *ap = std::string(main_task->t_cwd) + std::string("/") + std::string(pathname);
+ } else {
+ struct file *fp;
+
+ error = fget(dirfd, &fp);
+ if (error)
+ return error;
+
+ if(!fp->f_dentry)
+ return EBADF;
Oh no, you are returning here (and other places below) without fdrop(fp)...
Either remember to fdrop(fp) in all the missing places, or better yet, start using the better, C++-RAII-style, "fileref", which will mean you'll never have to worry about fdrop again.
++
+ if (!(fp->f_dentry->d_vnode->v_type & VDIR))
+ return ENOTDIR;
+
+ if (pathname)
+ *ap = std::string(fp->f_dentry->d_path) + std::string("/") + std::string(pathname);
+ else
+ *ap = std::string(fp->f_dentry->d_path);
+ fdrop(fp);
+ }
+ /* FIXME: Add support for AT_SYMLINK_NOFOLLOW */
+ if (flags && !(flags & AT_SYMLINK_NOFOLLOW))
+ return EINVAL;
+
+ auto absolute_path = new char[ap->length()+1];
+ strcpy(absolute_path, ap->c_str());
Why not just use ap->c_str directly - why do you need to copy it into absolute_path? ap->c_str() is already a nice, clean, C string, you can use....
+ auto absolute_path = new char[ap->length()+1];I did try to use it directly. Turns out, namei expects char*, not const char* which c_str() provides. It had to be either this change or change of function header ( add const ) to namei() , vfs_findroot() and so on.+ strcpy(absolute_path, ap->c_str());
Why not just use ap->c_str directly - why do you need to copy it into absolute_path? ap->c_str() is already a nice, clean, C string, you can use....
Thanks!
Nadav.
--
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 osv-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
++ std::string pathname = fp->f_dentry->d_path;
+ auto error = sys_utimensat(AT_FDCWD, pathname.c_str(), times, 0);
+ return error;
+}
+
int
sys_fallocate(struct file *fp, int mode, loff_t offset, loff_t len)
{
diff --git a/fs/vfs/vfs_vnode.c b/fs/vfs/vfs_vnode.c
index cbed3fc..98445f4 100644
--- a/fs/vfs/vfs_vnode.c
+++ b/fs/vfs/vfs_vnode.c
@@ -400,8 +400,8 @@ vn_settimes(struct vnode *vp, struct timespec times[2])
vap->va_atime = times[0];
vap->va_mtime = times[1];
- vap->va_mask = AT_ATIME | AT_MTIME;
-
+ vap->va_mask = ((times[0].tv_nsec == UTIME_OMIT) ? 0 : AT_ATIME)
+ | ((times[1].tv_nsec == UTIME_OMIT) ? 0 : AT_MTIME);
vn_lock(vp);
error = VOP_SETATTR(vp, vap);
vn_unlock(vp);
--
1.9.0
--
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 osv-dev+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.