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