The main purpose of this patch is to fix handling of unpause calls from PM while another call is ongoing. The solution to this problem sparked a full revision of the threading model, consisting of a large number of related changes: - all active worker threads are now always associated with a process, and every process has at most one active thread working for it; - the process lock is always held by a process's worker thread; - a process can now have both normal work and postponed PM work associated to it; - timer expiry and non-postponed PM work is done from the main thread; - filp garbage collection is done from a thread associated with VFS; - reboot calls from PM are now done from a thread associated with PM; - the DS events handler is protected from starting multiple threads; - support for a system worker thread has been removed; - the deadlock recovery thread has been replaced by a parameter to the worker_start() function; the number of worker threads has consequently been increased by one; - saving and restoring of global but per-thread variables is now centralized in worker_suspend() and worker_resume(); err_code is now saved and restored in all cases; - the concept of jobs has been removed, and job_m_in now points to a message stored in the worker thread structure instead; - the PM lock has been removed; - the separate exec lock has been replaced by a lock on the VM process, which was already being locked for exec calls anyway; - PM_UNPAUSE is now processed as a postponed PM request, from a thread associated with the target process; - the FP_DROP_WORK flag has been removed, since it is no longer more than just an optimization and only applied to processes operating on a pipe when getting killed; - assignment to "fp" now takes place only when obtaining new work in the main thread or a worker thread, when resuming execution of a thread, and in the special case of exiting processes during reboot; - there are no longer special cases where the yield() call is used to force a thread to run. Change-Id: I7a97b9b95c2450454a9b5318dfa0e6150d4e6858
215 lines
6.3 KiB
C
215 lines
6.3 KiB
C
/* This file takes care of those system calls that deal with time.
|
|
*
|
|
* The entry points into this file are
|
|
* do_utime: perform the UTIME system call
|
|
* do_utimens: perform the UTIMENS system call
|
|
*/
|
|
|
|
#include "fs.h"
|
|
#include <minix/callnr.h>
|
|
#include <minix/com.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include "file.h"
|
|
#include "path.h"
|
|
#include "param.h"
|
|
#include "vnode.h"
|
|
#include <minix/vfsif.h>
|
|
#include "vmnt.h"
|
|
|
|
#define UTIMENS_STYLE 0 /* utimes(2)/utimensat(2) style, named file */
|
|
#define FUTIMENS_STYLE 1 /* futimens(2)/futimes(2) style, file desc. */
|
|
|
|
/*===========================================================================*
|
|
* do_utime *
|
|
*===========================================================================*/
|
|
int do_utime(message *UNUSED(m_out))
|
|
{
|
|
/* Perform the utime(name, timep) system call. */
|
|
int r;
|
|
struct timespec actim, modtim, newactim, newmodtim;
|
|
struct vnode *vp;
|
|
struct vmnt *vmp;
|
|
char fullpath[PATH_MAX];
|
|
struct lookup resolve;
|
|
vir_bytes vname;
|
|
size_t vname_length, len;
|
|
|
|
vname = (vir_bytes) job_m_in.utime_file;
|
|
vname_length = (size_t) job_m_in.utime_length;
|
|
actim.tv_sec = job_m_in.utime_actime;
|
|
modtim.tv_sec = job_m_in.utime_modtime;
|
|
actim.tv_nsec = modtim.tv_nsec = 0;
|
|
|
|
/* Adjust for case of 'timep' being NULL;
|
|
* utime_strlen then holds the actual size: strlen(name)+1 */
|
|
len = vname_length;
|
|
if (len == 0) len = (size_t) job_m_in.utime_strlen;
|
|
|
|
lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
|
|
resolve.l_vmnt_lock = VMNT_READ;
|
|
resolve.l_vnode_lock = VNODE_READ;
|
|
|
|
/* Temporarily open the file */
|
|
if (fetch_name(vname, len, fullpath) != OK) return(err_code);
|
|
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
|
|
/* Only the owner of a file or the super user can change timestamps. */
|
|
r = OK;
|
|
if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) r = EPERM;
|
|
if (vname_length == 0 && r != OK) r = forbidden(fp, vp, W_BIT);
|
|
if (read_only(vp) != OK) r = EROFS; /* Not even su can touch if R/O */
|
|
if (r == OK) {
|
|
/* Issue request */
|
|
if (vname_length == 0) {
|
|
newactim = newmodtim = clock_timespec();
|
|
} else {
|
|
newactim = actim;
|
|
newmodtim = modtim;
|
|
}
|
|
r = req_utime(vp->v_fs_e, vp->v_inode_nr, &newactim, &newmodtim);
|
|
}
|
|
|
|
unlock_vnode(vp);
|
|
unlock_vmnt(vmp);
|
|
|
|
put_vnode(vp);
|
|
return(r);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* do_utimens *
|
|
*===========================================================================*/
|
|
int do_utimens(message *UNUSED(m_out))
|
|
{
|
|
/* Perform the utimens(name, times, flag) system call, and its friends.
|
|
* Implement a very large but not complete subset of the utimensat()
|
|
* Posix:2008/XOpen-7 function.
|
|
* Are handled all the following cases:
|
|
* . utimensat(AT_FDCWD, "/some/absolute/path", , )
|
|
* . utimensat(AT_FDCWD, "some/path", , )
|
|
* . utimens("anything", ) really special case of the above two
|
|
* . lutimens("anything", ) also really special case of the above
|
|
* . utimensat(fd, "/some/absolute/path", , ) although fd is useless here
|
|
* . futimens(fd, )
|
|
* Are not handled the following cases:
|
|
* . utimensat(fd, "some/path", , ) path to a file relative to some open fd
|
|
*/
|
|
int r, kind, lookup_flags;
|
|
struct vnode *vp;
|
|
struct filp *filp = NULL; /* initialization required by clueless GCC */
|
|
struct vmnt *vmp;
|
|
struct timespec actim, modtim, now, newactim, newmodtim;
|
|
char fullpath[PATH_MAX];
|
|
struct lookup resolve;
|
|
vir_bytes vname;
|
|
size_t vname_length;
|
|
|
|
memset(&now, 0, sizeof(now));
|
|
|
|
/* The case times==NULL is handled by the caller, replaced with UTIME_NOW */
|
|
actim.tv_sec = job_m_in.utime_actime;
|
|
actim.tv_nsec = job_m_in.utimens_ansec;
|
|
modtim.tv_sec = job_m_in.utime_modtime;
|
|
modtim.tv_nsec = job_m_in.utimens_mnsec;
|
|
|
|
if (job_m_in.utime_file != NULL) {
|
|
kind = UTIMENS_STYLE;
|
|
if (job_m_in.utimens_flags & ~AT_SYMLINK_NOFOLLOW)
|
|
return EINVAL; /* unknown flag */
|
|
/* Temporarily open the file */
|
|
vname = (vir_bytes) job_m_in.utime_file;
|
|
vname_length = (size_t) job_m_in.utime_length;
|
|
if (job_m_in.utimens_flags & AT_SYMLINK_NOFOLLOW)
|
|
lookup_flags = PATH_RET_SYMLINK;
|
|
else
|
|
lookup_flags = PATH_NOFLAGS;
|
|
lookup_init(&resolve, fullpath, lookup_flags, &vmp, &vp);
|
|
resolve.l_vmnt_lock = VMNT_READ;
|
|
resolve.l_vnode_lock = VNODE_READ;
|
|
/* Temporarily open the file */
|
|
if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
|
|
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
|
|
}
|
|
else {
|
|
kind = FUTIMENS_STYLE;
|
|
/* Change timestamps on already-opened fd. Is it valid? */
|
|
if (job_m_in.utimens_flags != 0)
|
|
return EINVAL; /* unknown flag */
|
|
if ((filp = get_filp(job_m_in.utimens_fd, VNODE_READ)) == NULL)
|
|
return err_code;
|
|
vp = filp->filp_vno;
|
|
}
|
|
|
|
r = OK;
|
|
/* Only the owner of a file or the super user can change timestamps. */
|
|
if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) r = EPERM;
|
|
/* Need write permission (or super user) to 'touch' the file */
|
|
if (r != OK && actim.tv_nsec == UTIME_NOW
|
|
&& modtim.tv_nsec == UTIME_NOW) r = forbidden(fp, vp, W_BIT);
|
|
if (read_only(vp) != OK) r = EROFS; /* Not even su can touch if R/O */
|
|
|
|
if (r == OK) {
|
|
/* Do we need to ask for current time? */
|
|
if (actim.tv_nsec == UTIME_NOW
|
|
|| actim.tv_nsec == UTIME_OMIT
|
|
|| modtim.tv_nsec == UTIME_NOW
|
|
|| modtim.tv_nsec == UTIME_OMIT) {
|
|
now = clock_timespec();
|
|
}
|
|
|
|
/* Build the request */
|
|
switch (actim.tv_nsec) {
|
|
case UTIME_NOW:
|
|
newactim = now;
|
|
break;
|
|
case UTIME_OMIT:
|
|
newactim.tv_nsec = UTIME_OMIT;
|
|
/* Be nice with old FS, put a sensible value in
|
|
* otherwise not used field for seconds
|
|
*/
|
|
newactim.tv_sec = now.tv_sec;
|
|
break;
|
|
default:
|
|
if ( (unsigned)actim.tv_nsec >= 1000000000)
|
|
r = EINVAL;
|
|
else
|
|
newactim = actim;
|
|
break;
|
|
}
|
|
switch (modtim.tv_nsec) {
|
|
case UTIME_NOW:
|
|
newmodtim = now;
|
|
break;
|
|
case UTIME_OMIT:
|
|
newmodtim.tv_nsec = UTIME_OMIT;
|
|
/* Be nice with old FS, put a sensible value */
|
|
newmodtim.tv_sec = now.tv_sec;
|
|
break;
|
|
default:
|
|
if ( (unsigned)modtim.tv_nsec >= 1000000000)
|
|
r = EINVAL;
|
|
else
|
|
newmodtim = modtim;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (r == OK)
|
|
/* Issue request */
|
|
r = req_utime(vp->v_fs_e, vp->v_inode_nr, &newactim, &newmodtim);
|
|
|
|
if (kind == UTIMENS_STYLE) {
|
|
/* Close the temporary */
|
|
unlock_vnode(vp);
|
|
unlock_vmnt(vmp);
|
|
put_vnode(vp);
|
|
}
|
|
else { /* Change timestamps on opened fd. */
|
|
unlock_filp(filp);
|
|
}
|
|
return r;
|
|
}
|