1433d6423SLionel Sambuc /* This file takes care of those system calls that deal with time.
2433d6423SLionel Sambuc *
3433d6423SLionel Sambuc * The entry points into this file are
4433d6423SLionel Sambuc * do_utimens: perform the UTIMENS system call
5433d6423SLionel Sambuc */
6433d6423SLionel Sambuc
7433d6423SLionel Sambuc #include "fs.h"
8433d6423SLionel Sambuc #include <minix/callnr.h>
9433d6423SLionel Sambuc #include <minix/com.h>
10433d6423SLionel Sambuc #include <time.h>
11433d6423SLionel Sambuc #include <string.h>
12433d6423SLionel Sambuc #include <sys/stat.h>
13433d6423SLionel Sambuc #include <fcntl.h>
14433d6423SLionel Sambuc #include "file.h"
15433d6423SLionel Sambuc #include "path.h"
16433d6423SLionel Sambuc #include "vnode.h"
17433d6423SLionel Sambuc #include <minix/vfsif.h>
18433d6423SLionel Sambuc #include "vmnt.h"
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc #define UTIMENS_STYLE 0 /* utimes(2)/utimensat(2) style, named file */
21433d6423SLionel Sambuc #define FUTIMENS_STYLE 1 /* futimens(2)/futimes(2) style, file desc. */
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc /*===========================================================================*
24433d6423SLionel Sambuc * do_utimens *
25433d6423SLionel Sambuc *===========================================================================*/
do_utimens(void)26433d6423SLionel Sambuc int do_utimens(void)
27433d6423SLionel Sambuc {
28433d6423SLionel Sambuc /* Perform the utimens(name, times, flag) system call, and its friends.
29433d6423SLionel Sambuc * Implement a very large but not complete subset of the utimensat()
30433d6423SLionel Sambuc * Posix:2008/XOpen-7 function.
31433d6423SLionel Sambuc * Are handled all the following cases:
32433d6423SLionel Sambuc * . utimensat(AT_FDCWD, "/some/absolute/path", , )
33433d6423SLionel Sambuc * . utimensat(AT_FDCWD, "some/path", , )
34433d6423SLionel Sambuc * . utimens("anything", ) really special case of the above two
35433d6423SLionel Sambuc * . lutimens("anything", ) also really special case of the above
36433d6423SLionel Sambuc * . utimensat(fd, "/some/absolute/path", , ) although fd is useless here
37433d6423SLionel Sambuc * . futimens(fd, )
38433d6423SLionel Sambuc * Are not handled the following cases:
39433d6423SLionel Sambuc * . utimensat(fd, "some/path", , ) path to a file relative to some open fd
40433d6423SLionel Sambuc */
41433d6423SLionel Sambuc int r, kind, lookup_flags;
42433d6423SLionel Sambuc struct vnode *vp;
43433d6423SLionel Sambuc struct filp *filp = NULL; /* initialization required by clueless GCC */
44433d6423SLionel Sambuc struct vmnt *vmp;
45433d6423SLionel Sambuc struct timespec actim, modtim, now, newactim, newmodtim;
46433d6423SLionel Sambuc char fullpath[PATH_MAX];
47433d6423SLionel Sambuc struct lookup resolve;
48433d6423SLionel Sambuc vir_bytes vname;
49433d6423SLionel Sambuc size_t vname_length;
50433d6423SLionel Sambuc
51433d6423SLionel Sambuc memset(&now, 0, sizeof(now));
52433d6423SLionel Sambuc
53433d6423SLionel Sambuc /* The case times==NULL is handled by the caller, replaced with UTIME_NOW */
54433d6423SLionel Sambuc actim.tv_sec = job_m_in.m_vfs_utimens.atime;
55433d6423SLionel Sambuc actim.tv_nsec = job_m_in.m_vfs_utimens.ansec;
56433d6423SLionel Sambuc modtim.tv_sec = job_m_in.m_vfs_utimens.mtime;
57433d6423SLionel Sambuc modtim.tv_nsec = job_m_in.m_vfs_utimens.mnsec;
58433d6423SLionel Sambuc
59433d6423SLionel Sambuc if (job_m_in.m_vfs_utimens.name != NULL) {
60433d6423SLionel Sambuc kind = UTIMENS_STYLE;
61433d6423SLionel Sambuc if (job_m_in.m_vfs_utimens.flags & ~AT_SYMLINK_NOFOLLOW)
62433d6423SLionel Sambuc return EINVAL; /* unknown flag */
63433d6423SLionel Sambuc /* Temporarily open the file */
64433d6423SLionel Sambuc vname = (vir_bytes) job_m_in.m_vfs_utimens.name;
65433d6423SLionel Sambuc vname_length = (size_t) job_m_in.m_vfs_utimens.len;
66433d6423SLionel Sambuc if (job_m_in.m_vfs_utimens.flags & AT_SYMLINK_NOFOLLOW)
67433d6423SLionel Sambuc lookup_flags = PATH_RET_SYMLINK;
68433d6423SLionel Sambuc else
69433d6423SLionel Sambuc lookup_flags = PATH_NOFLAGS;
70433d6423SLionel Sambuc lookup_init(&resolve, fullpath, lookup_flags, &vmp, &vp);
71433d6423SLionel Sambuc resolve.l_vmnt_lock = VMNT_READ;
72433d6423SLionel Sambuc resolve.l_vnode_lock = VNODE_READ;
73433d6423SLionel Sambuc /* Temporarily open the file */
74433d6423SLionel Sambuc if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
75433d6423SLionel Sambuc if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
76433d6423SLionel Sambuc }
77433d6423SLionel Sambuc else {
78433d6423SLionel Sambuc kind = FUTIMENS_STYLE;
79433d6423SLionel Sambuc /* Change timestamps on already-opened fd. Is it valid? */
80433d6423SLionel Sambuc if (job_m_in.m_vfs_utimens.flags != 0)
81433d6423SLionel Sambuc return EINVAL; /* unknown flag */
82433d6423SLionel Sambuc if ((filp = get_filp(job_m_in.m_vfs_utimens.fd, VNODE_READ)) == NULL)
83433d6423SLionel Sambuc return err_code;
84433d6423SLionel Sambuc vp = filp->filp_vno;
85433d6423SLionel Sambuc }
86433d6423SLionel Sambuc
87433d6423SLionel Sambuc r = OK;
88433d6423SLionel Sambuc /* Only the owner of a file or the super user can change timestamps. */
89433d6423SLionel Sambuc if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) r = EPERM;
90433d6423SLionel Sambuc /* Need write permission (or super user) to 'touch' the file */
91433d6423SLionel Sambuc if (r != OK && actim.tv_nsec == UTIME_NOW
92433d6423SLionel Sambuc && modtim.tv_nsec == UTIME_NOW) r = forbidden(fp, vp, W_BIT);
93433d6423SLionel Sambuc if (read_only(vp) != OK) r = EROFS; /* Not even su can touch if R/O */
94433d6423SLionel Sambuc
95433d6423SLionel Sambuc if (r == OK) {
96433d6423SLionel Sambuc /* Do we need to ask for current time? */
97433d6423SLionel Sambuc if (actim.tv_nsec == UTIME_NOW
98433d6423SLionel Sambuc || actim.tv_nsec == UTIME_OMIT
99433d6423SLionel Sambuc || modtim.tv_nsec == UTIME_NOW
100433d6423SLionel Sambuc || modtim.tv_nsec == UTIME_OMIT) {
101*ad80a203SDavid van Moolenbroek (void)clock_time(&now);
102433d6423SLionel Sambuc }
103433d6423SLionel Sambuc
104433d6423SLionel Sambuc /* Build the request */
105433d6423SLionel Sambuc switch (actim.tv_nsec) {
106433d6423SLionel Sambuc case UTIME_NOW:
107433d6423SLionel Sambuc newactim = now;
108433d6423SLionel Sambuc break;
109433d6423SLionel Sambuc case UTIME_OMIT:
110433d6423SLionel Sambuc newactim.tv_nsec = UTIME_OMIT;
111433d6423SLionel Sambuc /* Be nice with old FS, put a sensible value in
112433d6423SLionel Sambuc * otherwise not used field for seconds
113433d6423SLionel Sambuc */
114433d6423SLionel Sambuc newactim.tv_sec = now.tv_sec;
115433d6423SLionel Sambuc break;
116433d6423SLionel Sambuc default:
117433d6423SLionel Sambuc if ( (unsigned)actim.tv_nsec >= 1000000000)
118433d6423SLionel Sambuc r = EINVAL;
119433d6423SLionel Sambuc else
120433d6423SLionel Sambuc newactim = actim;
121433d6423SLionel Sambuc break;
122433d6423SLionel Sambuc }
123433d6423SLionel Sambuc switch (modtim.tv_nsec) {
124433d6423SLionel Sambuc case UTIME_NOW:
125433d6423SLionel Sambuc newmodtim = now;
126433d6423SLionel Sambuc break;
127433d6423SLionel Sambuc case UTIME_OMIT:
128433d6423SLionel Sambuc newmodtim.tv_nsec = UTIME_OMIT;
129433d6423SLionel Sambuc /* Be nice with old FS, put a sensible value */
130433d6423SLionel Sambuc newmodtim.tv_sec = now.tv_sec;
131433d6423SLionel Sambuc break;
132433d6423SLionel Sambuc default:
133433d6423SLionel Sambuc if ( (unsigned)modtim.tv_nsec >= 1000000000)
134433d6423SLionel Sambuc r = EINVAL;
135433d6423SLionel Sambuc else
136433d6423SLionel Sambuc newmodtim = modtim;
137433d6423SLionel Sambuc break;
138433d6423SLionel Sambuc }
139433d6423SLionel Sambuc }
140433d6423SLionel Sambuc
141433d6423SLionel Sambuc if (r == OK)
142433d6423SLionel Sambuc /* Issue request */
143433d6423SLionel Sambuc r = req_utime(vp->v_fs_e, vp->v_inode_nr, &newactim, &newmodtim);
144433d6423SLionel Sambuc
145433d6423SLionel Sambuc if (kind == UTIMENS_STYLE) {
146433d6423SLionel Sambuc /* Close the temporary */
147433d6423SLionel Sambuc unlock_vnode(vp);
148433d6423SLionel Sambuc unlock_vmnt(vmp);
149433d6423SLionel Sambuc put_vnode(vp);
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc else { /* Change timestamps on opened fd. */
152433d6423SLionel Sambuc unlock_filp(filp);
153433d6423SLionel Sambuc }
154433d6423SLionel Sambuc return r;
155433d6423SLionel Sambuc }
156