1 /* This file takes care of those system calls that deal with time. 2 * 3 * The entry points into this file are 4 * do_utimens: perform the UTIMENS system call 5 */ 6 7 #include "fs.h" 8 #include <minix/callnr.h> 9 #include <minix/com.h> 10 #include <time.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <fcntl.h> 14 #include "file.h" 15 #include "path.h" 16 #include "vnode.h" 17 #include <minix/vfsif.h> 18 #include "vmnt.h" 19 20 #define UTIMENS_STYLE 0 /* utimes(2)/utimensat(2) style, named file */ 21 #define FUTIMENS_STYLE 1 /* futimens(2)/futimes(2) style, file desc. */ 22 23 /*===========================================================================* 24 * do_utimens * 25 *===========================================================================*/ 26 int do_utimens(void) 27 { 28 /* Perform the utimens(name, times, flag) system call, and its friends. 29 * Implement a very large but not complete subset of the utimensat() 30 * Posix:2008/XOpen-7 function. 31 * Are handled all the following cases: 32 * . utimensat(AT_FDCWD, "/some/absolute/path", , ) 33 * . utimensat(AT_FDCWD, "some/path", , ) 34 * . utimens("anything", ) really special case of the above two 35 * . lutimens("anything", ) also really special case of the above 36 * . utimensat(fd, "/some/absolute/path", , ) although fd is useless here 37 * . futimens(fd, ) 38 * Are not handled the following cases: 39 * . utimensat(fd, "some/path", , ) path to a file relative to some open fd 40 */ 41 int r, kind, lookup_flags; 42 struct vnode *vp; 43 struct filp *filp = NULL; /* initialization required by clueless GCC */ 44 struct vmnt *vmp; 45 struct timespec actim, modtim, now, newactim, newmodtim; 46 char fullpath[PATH_MAX]; 47 struct lookup resolve; 48 vir_bytes vname; 49 size_t vname_length; 50 51 memset(&now, 0, sizeof(now)); 52 53 /* The case times==NULL is handled by the caller, replaced with UTIME_NOW */ 54 actim.tv_sec = job_m_in.m_vfs_utimens.atime; 55 actim.tv_nsec = job_m_in.m_vfs_utimens.ansec; 56 modtim.tv_sec = job_m_in.m_vfs_utimens.mtime; 57 modtim.tv_nsec = job_m_in.m_vfs_utimens.mnsec; 58 59 if (job_m_in.m_vfs_utimens.name != NULL) { 60 kind = UTIMENS_STYLE; 61 if (job_m_in.m_vfs_utimens.flags & ~AT_SYMLINK_NOFOLLOW) 62 return EINVAL; /* unknown flag */ 63 /* Temporarily open the file */ 64 vname = (vir_bytes) job_m_in.m_vfs_utimens.name; 65 vname_length = (size_t) job_m_in.m_vfs_utimens.len; 66 if (job_m_in.m_vfs_utimens.flags & AT_SYMLINK_NOFOLLOW) 67 lookup_flags = PATH_RET_SYMLINK; 68 else 69 lookup_flags = PATH_NOFLAGS; 70 lookup_init(&resolve, fullpath, lookup_flags, &vmp, &vp); 71 resolve.l_vmnt_lock = VMNT_READ; 72 resolve.l_vnode_lock = VNODE_READ; 73 /* Temporarily open the file */ 74 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code); 75 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 76 } 77 else { 78 kind = FUTIMENS_STYLE; 79 /* Change timestamps on already-opened fd. Is it valid? */ 80 if (job_m_in.m_vfs_utimens.flags != 0) 81 return EINVAL; /* unknown flag */ 82 if ((filp = get_filp(job_m_in.m_vfs_utimens.fd, VNODE_READ)) == NULL) 83 return err_code; 84 vp = filp->filp_vno; 85 } 86 87 r = OK; 88 /* Only the owner of a file or the super user can change timestamps. */ 89 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) r = EPERM; 90 /* Need write permission (or super user) to 'touch' the file */ 91 if (r != OK && actim.tv_nsec == UTIME_NOW 92 && modtim.tv_nsec == UTIME_NOW) r = forbidden(fp, vp, W_BIT); 93 if (read_only(vp) != OK) r = EROFS; /* Not even su can touch if R/O */ 94 95 if (r == OK) { 96 /* Do we need to ask for current time? */ 97 if (actim.tv_nsec == UTIME_NOW 98 || actim.tv_nsec == UTIME_OMIT 99 || modtim.tv_nsec == UTIME_NOW 100 || modtim.tv_nsec == UTIME_OMIT) { 101 now = clock_timespec(); 102 } 103 104 /* Build the request */ 105 switch (actim.tv_nsec) { 106 case UTIME_NOW: 107 newactim = now; 108 break; 109 case UTIME_OMIT: 110 newactim.tv_nsec = UTIME_OMIT; 111 /* Be nice with old FS, put a sensible value in 112 * otherwise not used field for seconds 113 */ 114 newactim.tv_sec = now.tv_sec; 115 break; 116 default: 117 if ( (unsigned)actim.tv_nsec >= 1000000000) 118 r = EINVAL; 119 else 120 newactim = actim; 121 break; 122 } 123 switch (modtim.tv_nsec) { 124 case UTIME_NOW: 125 newmodtim = now; 126 break; 127 case UTIME_OMIT: 128 newmodtim.tv_nsec = UTIME_OMIT; 129 /* Be nice with old FS, put a sensible value */ 130 newmodtim.tv_sec = now.tv_sec; 131 break; 132 default: 133 if ( (unsigned)modtim.tv_nsec >= 1000000000) 134 r = EINVAL; 135 else 136 newmodtim = modtim; 137 break; 138 } 139 } 140 141 if (r == OK) 142 /* Issue request */ 143 r = req_utime(vp->v_fs_e, vp->v_inode_nr, &newactim, &newmodtim); 144 145 if (kind == UTIMENS_STYLE) { 146 /* Close the temporary */ 147 unlock_vnode(vp); 148 unlock_vmnt(vmp); 149 put_vnode(vp); 150 } 151 else { /* Change timestamps on opened fd. */ 152 unlock_filp(filp); 153 } 154 return r; 155 } 156