tar: use utime() to restore timestamps
[minix.git] / servers / vfs / protect.c
blob70a4536e223f1341c789e2e76f4b28d878f29322
1 /* This file deals with protection in the file system. It contains the code
2 * for four system calls that relate to protection.
4 * The entry points into this file are
5 * do_chmod: perform the CHMOD and FCHMOD system calls
6 * do_chown: perform the CHOWN and FCHOWN system calls
7 * do_umask: perform the UMASK system call
8 * do_access: perform the ACCESS system call
9 */
11 #include "fs.h"
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <minix/callnr.h>
15 #include "file.h"
16 #include "fproc.h"
17 #include "path.h"
18 #include "param.h"
19 #include <minix/vfsif.h>
20 #include "vnode.h"
21 #include "vmnt.h"
23 /*===========================================================================*
24 * do_chmod *
25 *===========================================================================*/
26 int do_chmod()
28 /* Perform the chmod(name, mode) and fchmod(fd, mode) system calls.
29 * syscall might provide 'name' embedded in the message.
32 struct filp *flp;
33 struct vnode *vp;
34 struct vmnt *vmp;
35 int r, rfd;
36 mode_t result_mode;
37 char fullpath[PATH_MAX];
38 struct lookup resolve;
39 vir_bytes vname;
40 size_t vname_length;
41 mode_t new_mode;
43 flp = NULL;
44 vname = (vir_bytes) job_m_in.name;
45 vname_length = (size_t) job_m_in.name_length;
46 rfd = job_m_in.fd;
47 new_mode = (mode_t) job_m_in.mode;
49 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
50 resolve.l_vmnt_lock = VMNT_READ;
51 resolve.l_vnode_lock = VNODE_WRITE;
53 if (job_call_nr == CHMOD) {
54 /* Temporarily open the file */
55 if (copy_name(vname_length, fullpath) != OK) {
56 /* Direct copy failed, try fetching from user space */
57 if (fetch_name(vname, vname_length, fullpath) != OK)
58 return(err_code);
60 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
61 } else { /* call_nr == FCHMOD */
62 /* File is already opened; get a pointer to vnode from filp. */
63 if ((flp = get_filp(rfd, VNODE_WRITE)) == NULL) return(err_code);
64 vp = flp->filp_vno;
65 dup_vnode(vp);
68 /* Only the owner or the super_user may change the mode of a file.
69 * No one may change the mode of a file on a read-only file system.
71 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
72 r = EPERM;
73 else
74 r = read_only(vp);
76 if (r == OK) {
77 /* Now make the change. Clear setgid bit if file is not in caller's
78 * group */
79 if (fp->fp_effuid != SU_UID && vp->v_gid != fp->fp_effgid)
80 new_mode &= ~I_SET_GID_BIT;
82 r = req_chmod(vp->v_fs_e, vp->v_inode_nr, new_mode, &result_mode);
83 if (r == OK)
84 vp->v_mode = result_mode;
87 if (job_call_nr == CHMOD) {
88 unlock_vnode(vp);
89 unlock_vmnt(vmp);
90 } else { /* FCHMOD */
91 unlock_filp(flp);
94 put_vnode(vp);
95 return(r);
99 /*===========================================================================*
100 * do_chown *
101 *===========================================================================*/
102 int do_chown()
104 /* Perform the chown(path, owner, group) and fchmod(fd, owner, group) system
105 * calls. */
106 struct filp *flp;
107 struct vnode *vp;
108 struct vmnt *vmp;
109 int r, rfd;
110 uid_t uid, new_uid;
111 gid_t gid, new_gid;
112 mode_t new_mode;
113 char fullpath[PATH_MAX];
114 struct lookup resolve;
115 vir_bytes vname1;
116 size_t vname1_length;
118 flp = NULL;
119 vname1 = (vir_bytes) job_m_in.name1;
120 vname1_length = (size_t) job_m_in.name1_length;
121 rfd = job_m_in.fd;
122 uid = job_m_in.owner;
123 gid = job_m_in.group;
125 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
126 resolve.l_vmnt_lock = VMNT_READ;
127 resolve.l_vnode_lock = VNODE_WRITE;
129 if (job_call_nr == CHOWN) {
130 /* Temporarily open the file. */
131 if (fetch_name(vname1, vname1_length, fullpath) != OK)
132 return(err_code);
133 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
134 } else { /* call_nr == FCHOWN */
135 /* File is already opened; get a pointer to the vnode from filp. */
136 if ((flp = get_filp(rfd, VNODE_WRITE)) == NULL)
137 return(err_code);
138 vp = flp->filp_vno;
139 dup_vnode(vp);
142 r = read_only(vp);
143 if (r == OK) {
144 /* FS is R/W. Whether call is allowed depends on ownership, etc. */
145 /* The super user can do anything, so check permissions only if we're
146 a regular user. */
147 if (fp->fp_effuid != SU_UID) {
148 /* Regular users can only change groups of their own files. */
149 if (vp->v_uid != fp->fp_effuid) r = EPERM;
150 if (vp->v_uid != uid) r = EPERM; /* no giving away */
151 if (fp->fp_effgid != gid) r = EPERM;
155 if (r == OK) {
156 /* Do not change uid/gid if new uid/gid is -1. */
157 new_uid = (uid == (uid_t)-1 ? vp->v_uid : uid);
158 new_gid = (gid == (gid_t)-1 ? vp->v_gid : gid);
160 if (new_uid > UID_MAX || new_gid > GID_MAX)
161 r = EINVAL;
162 else if ((r = req_chown(vp->v_fs_e, vp->v_inode_nr, new_uid, new_gid,
163 &new_mode)) == OK) {
164 vp->v_uid = new_uid;
165 vp->v_gid = new_gid;
166 vp->v_mode = new_mode;
170 if (job_call_nr == CHOWN) {
171 unlock_vnode(vp);
172 unlock_vmnt(vmp);
173 } else { /* FCHOWN */
174 unlock_filp(flp);
177 put_vnode(vp);
178 return(r);
181 /*===========================================================================*
182 * do_umask *
183 *===========================================================================*/
184 int do_umask()
186 /* Perform the umask(co_mode) system call. */
187 mode_t complement, new_umask;
189 new_umask = job_m_in.co_mode;
191 complement = ~fp->fp_umask; /* set 'r' to complement of old mask */
192 fp->fp_umask = ~(new_umask & RWX_MODES);
193 return(complement); /* return complement of old mask */
197 /*===========================================================================*
198 * do_access *
199 *===========================================================================*/
200 int do_access()
202 /* Perform the access(name, mode) system call.
203 * syscall might provide 'name' embedded in the message.
205 int r;
206 struct vnode *vp;
207 struct vmnt *vmp;
208 char fullpath[PATH_MAX];
209 struct lookup resolve;
210 vir_bytes vname;
211 size_t vname_length;
212 mode_t access;
214 vname = (vir_bytes) job_m_in.name;
215 vname_length = (size_t) job_m_in.name_length;
216 access = job_m_in.mode;
218 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
219 resolve.l_vmnt_lock = VMNT_READ;
220 resolve.l_vnode_lock = VNODE_READ;
222 /* First check to see if the mode is correct. */
223 if ( (access & ~(R_OK | W_OK | X_OK)) != 0 && access != F_OK)
224 return(EINVAL);
226 /* Temporarily open the file. */
227 if (copy_name(vname_length, fullpath) != OK) {
228 /* Direct copy failed, try fetching from user space */
229 if (fetch_name(vname, vname_length, fullpath) != OK)
230 return(err_code);
232 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
234 r = forbidden(fp, vp, access);
236 unlock_vnode(vp);
237 unlock_vmnt(vmp);
239 put_vnode(vp);
240 return(r);
244 /*===========================================================================*
245 * forbidden *
246 *===========================================================================*/
247 int forbidden(struct fproc *rfp, struct vnode *vp, mode_t access_desired)
249 /* Given a pointer to an vnode, 'vp', and the access desired, determine
250 * if the access is allowed, and if not why not. The routine looks up the
251 * caller's uid in the 'fproc' table. If access is allowed, OK is returned
252 * if it is forbidden, EACCES is returned.
255 register mode_t bits, perm_bits;
256 uid_t uid;
257 gid_t gid;
258 int r, shift;
260 if (vp->v_uid == (uid_t) -1 || vp->v_gid == (gid_t) -1) return(EACCES);
262 /* Isolate the relevant rwx bits from the mode. */
263 bits = vp->v_mode;
264 uid = (job_call_nr == ACCESS ? rfp->fp_realuid : rfp->fp_effuid);
265 gid = (job_call_nr == ACCESS ? rfp->fp_realgid : rfp->fp_effgid);
267 if (uid == SU_UID) {
268 /* Grant read and write permission. Grant search permission for
269 * directories. Grant execute permission (for non-directories) if
270 * and only if one of the 'X' bits is set.
272 if ( S_ISDIR(bits) || bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT))
273 perm_bits = R_BIT | W_BIT | X_BIT;
274 else
275 perm_bits = R_BIT | W_BIT;
276 } else {
277 if (uid == vp->v_uid) shift = 6; /* owner */
278 else if (gid == vp->v_gid) shift = 3; /* group */
279 else if (in_group(fp, vp->v_gid) == OK) shift = 3; /* suppl. groups */
280 else shift = 0; /* other */
281 perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT);
284 /* If access desired is not a subset of what is allowed, it is refused. */
285 r = OK;
286 if ((perm_bits | access_desired) != perm_bits) r = EACCES;
288 /* Check to see if someone is trying to write on a file system that is
289 * mounted read-only.
291 if (r == OK)
292 if (access_desired & W_BIT)
293 r = read_only(vp);
295 return(r);
298 /*===========================================================================*
299 * read_only *
300 *===========================================================================*/
301 int read_only(vp)
302 struct vnode *vp; /* ptr to inode whose file sys is to be cked */
304 /* Check to see if the file system on which the inode 'ip' resides is mounted
305 * read only. If so, return EROFS, else return OK.
307 return((vp->v_vmnt->m_flags & VMNT_READONLY) ? EROFS : OK);