xref: /minix3/minix/servers/vfs/time.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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