xref: /minix3/minix/servers/vfs/stadir.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 /* This file contains the code for performing four system calls relating to
2  * status and directories.
3  *
4  * The entry points into this file are
5  *   do_chdir:	perform the CHDIR system call
6  *   do_chroot:	perform the CHROOT system call
7  *   do_lstat:  perform the LSTAT system call
8  *   do_stat:	perform the STAT system call
9  *   do_fstat:	perform the FSTAT system call
10  *   do_statvfs:    perform the STATVFS1 system call
11  *   do_fstatvfs:   perform the FSTATVFS1 system call
12  *   do_getvfsstat: perform the GETVFSSTAT system call
13  */
14 
15 #include "fs.h"
16 #include <sys/stat.h>
17 #include <minix/com.h>
18 #include <minix/u64.h>
19 #include <string.h>
20 #include "file.h"
21 #include "path.h"
22 #include <minix/vfsif.h>
23 #include <minix/callnr.h>
24 #include "vnode.h"
25 #include "vmnt.h"
26 
27 static int change_into(struct vnode **iip, struct vnode *vp);
28 
29 /*===========================================================================*
30  *				do_fchdir				     *
31  *===========================================================================*/
do_fchdir(void)32 int do_fchdir(void)
33 {
34   /* Change directory on already-opened fd. */
35   struct filp *rfilp;
36   int r, rfd;
37 
38   rfd = job_m_in.m_lc_vfs_fchdir.fd;
39 
40   /* Is the file descriptor valid? */
41   if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
42   r = change_into(&fp->fp_wd, rfilp->filp_vno);
43   unlock_filp(rfilp);
44   return(r);
45 }
46 
47 /*===========================================================================*
48  *				do_chdir				     *
49  *===========================================================================*/
do_chdir(void)50 int do_chdir(void)
51 {
52 /* Perform the chdir(name) system call.
53  * syscall might provide 'name' embedded in the message.
54  */
55 
56   int r;
57   struct vnode *vp;
58   struct vmnt *vmp;
59   char fullpath[PATH_MAX];
60   struct lookup resolve;
61 
62   if (copy_path(fullpath, sizeof(fullpath)) != OK)
63 	return(err_code);
64 
65   /* Try to open the directory */
66   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
67   resolve.l_vmnt_lock = VMNT_READ;
68   resolve.l_vnode_lock = VNODE_READ;
69   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
70 
71   r = change_into(&fp->fp_wd, vp);
72 
73   unlock_vnode(vp);
74   unlock_vmnt(vmp);
75   put_vnode(vp);
76 
77   return(r);
78 }
79 
80 /*===========================================================================*
81  *				do_chroot				     *
82  *===========================================================================*/
do_chroot(void)83 int do_chroot(void)
84 {
85 /* Perform the chroot(name) system call.
86  * syscall might provide 'name' embedded in the message.
87  */
88   int r;
89   struct vnode *vp;
90   struct vmnt *vmp;
91   char fullpath[PATH_MAX];
92   struct lookup resolve;
93 
94   if (!super_user) return(EPERM);	/* only su may chroot() */
95 
96   if (copy_path(fullpath, sizeof(fullpath)) != OK)
97 	return(err_code);
98 
99   /* Try to open the directory */
100   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
101   resolve.l_vmnt_lock = VMNT_READ;
102   resolve.l_vnode_lock = VNODE_READ;
103   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
104 
105   r = change_into(&fp->fp_rd, vp);
106 
107   unlock_vnode(vp);
108   unlock_vmnt(vmp);
109   put_vnode(vp);
110 
111   return(r);
112 }
113 
114 /*===========================================================================*
115  *				change_into				     *
116  *===========================================================================*/
change_into(struct vnode ** result,struct vnode * vp)117 static int change_into(struct vnode **result, struct vnode *vp)
118 {
119   int r;
120 
121   if (*result == vp) return(OK);	/* Nothing to do */
122 
123   /* It must be a directory and also be searchable */
124   if (!S_ISDIR(vp->v_mode))
125 	r = ENOTDIR;
126   else
127 	r = forbidden(fp, vp, X_BIT);	/* Check if dir is searchable*/
128   if (r != OK) return(r);
129 
130   /* Everything is OK.  Make the change. */
131   put_vnode(*result);		/* release the old directory */
132   dup_vnode(vp);
133   *result = vp;			/* acquire the new one */
134   return(OK);
135 }
136 
137 /*===========================================================================*
138  *				do_stat					     *
139  *===========================================================================*/
do_stat(void)140 int do_stat(void)
141 {
142 /* Perform the stat(name, buf) system call. */
143   int r;
144   struct vnode *vp;
145   struct vmnt *vmp;
146   char fullpath[PATH_MAX];
147   struct lookup resolve;
148   vir_bytes vname1, statbuf;
149   size_t vname1_length;
150 
151   vname1 = job_m_in.m_lc_vfs_stat.name;
152   vname1_length = job_m_in.m_lc_vfs_stat.len;
153   statbuf = job_m_in.m_lc_vfs_stat.buf;
154 
155   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
156   resolve.l_vmnt_lock = VMNT_READ;
157   resolve.l_vnode_lock = VNODE_READ;
158 
159   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
160   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
161   r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf);
162 
163   unlock_vnode(vp);
164   unlock_vmnt(vmp);
165 
166   put_vnode(vp);
167   return r;
168 }
169 
170 /*===========================================================================*
171  *				do_fstat				     *
172  *===========================================================================*/
do_fstat(void)173 int do_fstat(void)
174 {
175 /* Perform the fstat(fd, buf) system call. */
176   register struct filp *rfilp;
177   int r, rfd;
178   vir_bytes statbuf;
179 
180   statbuf = job_m_in.m_lc_vfs_fstat.buf;
181   rfd = job_m_in.m_lc_vfs_fstat.fd;
182 
183   /* Is the file descriptor valid? */
184   if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
185 
186   r = req_stat(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr,
187 	       who_e, statbuf);
188 
189   unlock_filp(rfilp);
190 
191   return(r);
192 }
193 
194 /*===========================================================================*
195  *				update_statvfs				     *
196  *===========================================================================*/
update_statvfs(struct vmnt * vmp,struct statvfs * buf)197 int update_statvfs(struct vmnt *vmp, struct statvfs *buf)
198 {
199 /* Get statistics from a file system, and cache part of the results. */
200   int r;
201 
202   if ((r = req_statvfs(vmp->m_fs_e, buf)) != OK)
203 	return r;
204 
205   vmp->m_stats.f_flag = buf->f_flag;
206   vmp->m_stats.f_bsize = buf->f_bsize;
207   vmp->m_stats.f_frsize = buf->f_frsize;
208   vmp->m_stats.f_iosize = buf->f_iosize;
209 
210   vmp->m_stats.f_blocks = buf->f_blocks;
211   vmp->m_stats.f_bfree = buf->f_bfree;
212   vmp->m_stats.f_bavail = buf->f_bavail;
213   vmp->m_stats.f_bresvd = buf->f_bresvd;
214 
215   vmp->m_stats.f_files = buf->f_files;
216   vmp->m_stats.f_ffree = buf->f_ffree;
217   vmp->m_stats.f_favail = buf->f_favail;
218   vmp->m_stats.f_fresvd = buf->f_fresvd;
219 
220   vmp->m_stats.f_syncreads = buf->f_syncreads;
221   vmp->m_stats.f_syncwrites = buf->f_syncwrites;
222 
223   vmp->m_stats.f_asyncreads = buf->f_asyncreads;
224   vmp->m_stats.f_asyncwrites = buf->f_asyncwrites;
225 
226   vmp->m_stats.f_namemax = buf->f_namemax;
227 
228   return OK;
229 }
230 
231 /*===========================================================================*
232  *				fill_statvfs				     *
233  *===========================================================================*/
fill_statvfs(struct vmnt * vmp,endpoint_t endpt,vir_bytes buf_addr,int flags)234 static int fill_statvfs(struct vmnt *vmp, endpoint_t endpt, vir_bytes buf_addr,
235 	int flags)
236 {
237 /* Fill a statvfs structure in a userspace process.  First let the target file
238  * server fill in most fields, or use the cached copy if ST_NOWAIT is given.
239  * Then fill in some remaining fields with local information.  Finally, copy
240  * the result to user space.
241  */
242   struct statvfs buf;
243 
244   if (!(flags & ST_NOWAIT)) {
245 	/* Get fresh statistics from the file system. */
246 	if (update_statvfs(vmp, &buf) != OK)
247 		return EIO;
248   } else {
249 	/* Use the cached statistics. */
250 	memset(&buf, 0, sizeof(buf));
251 
252 	buf.f_flag = vmp->m_stats.f_flag;
253 	buf.f_bsize = vmp->m_stats.f_bsize;
254 	buf.f_frsize = vmp->m_stats.f_frsize;
255 	buf.f_iosize = vmp->m_stats.f_iosize;
256 
257 	buf.f_blocks = vmp->m_stats.f_blocks;
258 	buf.f_bfree = vmp->m_stats.f_bfree;
259 	buf.f_bavail = vmp->m_stats.f_bavail;
260 	buf.f_bresvd = vmp->m_stats.f_bresvd;
261 
262 	buf.f_files = vmp->m_stats.f_files;
263 	buf.f_ffree = vmp->m_stats.f_ffree;
264 	buf.f_favail = vmp->m_stats.f_favail;
265 	buf.f_fresvd = vmp->m_stats.f_fresvd;
266 
267 	buf.f_syncreads = vmp->m_stats.f_syncreads;
268 	buf.f_syncwrites = vmp->m_stats.f_syncwrites;
269 
270 	buf.f_asyncreads = vmp->m_stats.f_asyncreads;
271 	buf.f_asyncwrites = vmp->m_stats.f_asyncwrites;
272 
273 	buf.f_namemax = vmp->m_stats.f_namemax;
274   }
275 
276   if (vmp->m_flags & VMNT_READONLY)
277 	buf.f_flag |= ST_RDONLY;
278 
279   buf.f_fsid = (unsigned long)vmp->m_dev;
280   buf.f_fsidx.__fsid_val[0] = (long)vmp->m_dev; /* This is what is done on NetBSD */
281   buf.f_fsidx.__fsid_val[1] = 0; /* Here they convert the FS type name into a number. */
282 
283   strlcpy(buf.f_fstypename, vmp->m_fstype, sizeof(buf.f_fstypename));
284   strlcpy(buf.f_mntonname, vmp->m_mount_path, sizeof(buf.f_mntonname));
285   strlcpy(buf.f_mntfromname, vmp->m_mount_dev, sizeof(buf.f_mntfromname));
286 
287   return sys_datacopy_wrapper(SELF, (vir_bytes) &buf,
288 	endpt, buf_addr, sizeof(buf));
289 }
290 
291 /*===========================================================================*
292  *				do_statvfs				     *
293  *===========================================================================*/
do_statvfs(void)294 int do_statvfs(void)
295 {
296 /* Perform the statvfs1(name, buf, flags) system call. */
297   int r, flags;
298   struct vnode *vp;
299   struct vmnt *vmp;
300   char fullpath[PATH_MAX];
301   struct lookup resolve;
302   vir_bytes vname1, statbuf;
303   size_t vname1_length;
304 
305   vname1 = job_m_in.m_lc_vfs_statvfs1.name;
306   vname1_length = job_m_in.m_lc_vfs_statvfs1.len;
307   statbuf = job_m_in.m_lc_vfs_statvfs1.buf;
308   flags = job_m_in.m_lc_vfs_statvfs1.flags;
309 
310   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
311   resolve.l_vmnt_lock = VMNT_READ;
312   resolve.l_vnode_lock = VNODE_READ;
313 
314   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
315   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
316   r = fill_statvfs(vp->v_vmnt, who_e, statbuf, flags);
317 
318   unlock_vnode(vp);
319   unlock_vmnt(vmp);
320 
321   put_vnode(vp);
322   return r;
323 }
324 
325 /*===========================================================================*
326  *				do_fstatvfs				     *
327  *===========================================================================*/
do_fstatvfs(void)328 int do_fstatvfs(void)
329 {
330 /* Perform the fstatvfs1(fd, buf, flags) system call. */
331   register struct filp *rfilp;
332   int r, rfd, flags;
333   vir_bytes statbuf;
334 
335   rfd = job_m_in.m_lc_vfs_statvfs1.fd;
336   statbuf = job_m_in.m_lc_vfs_statvfs1.buf;
337   flags = job_m_in.m_lc_vfs_statvfs1.flags;
338 
339   /* Is the file descriptor valid? */
340   if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
341   r = fill_statvfs(rfilp->filp_vno->v_vmnt, who_e, statbuf, flags);
342 
343   unlock_filp(rfilp);
344 
345   return(r);
346 }
347 
348 /*===========================================================================*
349  *				do_getvfsstat				     *
350  *===========================================================================*/
do_getvfsstat(void)351 int do_getvfsstat(void)
352 {
353 /* Perform the getvfsstat(buf, bufsize, flags) system call. */
354   struct vmnt *vmp;
355   vir_bytes buf;
356   size_t bufsize;
357   int r, flags, count, do_lock;
358 
359   buf = job_m_in.m_lc_vfs_getvfsstat.buf;
360   bufsize = job_m_in.m_lc_vfs_getvfsstat.len;
361   flags = job_m_in.m_lc_vfs_getvfsstat.flags;
362 
363   count = 0;
364 
365   if (buf != 0) {
366 	/* We only need to lock target file systems if we are going to query
367 	 * them.  This will only happen if ST_NOWAIT is not given.  If we do
368 	 * not lock, we rely on the VMNT_CANSTAT flag to protect us from
369 	 * concurrent (un)mount operations.  Note that procfs relies on
370 	 * ST_NOWAIT calls being lock free, as it is a file system itself.
371 	 */
372 	do_lock = !(flags & ST_NOWAIT);
373 
374 	for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
375 		/* If there is no more space, return the count so far. */
376 		if (bufsize < sizeof(struct statvfs))
377 			break;
378 
379 		/* Lock the file system before checking any fields. */
380 		if (do_lock && (r = lock_vmnt(vmp, VMNT_READ)) != OK)
381 			return r;
382 
383 		/* Obtain information for this file system, if it is in use and
384 		 * can be reported.  File systems that are being (un)mounted
385 		 * are skipped, as is PFS.  The fill call will block only if
386 		 * ST_NOWAIT was not given.
387 		 */
388 		if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT)) {
389 			if ((r = fill_statvfs(vmp, who_e, buf, flags)) != OK) {
390 				if (do_lock)
391 					unlock_vmnt(vmp);
392 
393 				return r;
394 			}
395 
396 			count++;
397 			buf += sizeof(struct statvfs);
398 			bufsize -= sizeof(struct statvfs);
399 		}
400 
401 		if (do_lock)
402 			unlock_vmnt(vmp);
403 	}
404   } else {
405 	/* Just report a file system count.  No need to lock, as above. */
406 	for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
407 		if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT))
408 			count++;
409 	}
410   }
411 
412   return count;
413 }
414 
415 /*===========================================================================*
416  *                             do_lstat					     *
417  *===========================================================================*/
do_lstat(void)418 int do_lstat(void)
419 {
420 /* Perform the lstat(name, buf) system call. */
421   struct vnode *vp;
422   struct vmnt *vmp;
423   int r;
424   char fullpath[PATH_MAX];
425   struct lookup resolve;
426   vir_bytes vname1, statbuf;
427   size_t vname1_length;
428 
429   vname1 = job_m_in.m_lc_vfs_stat.name;
430   vname1_length = job_m_in.m_lc_vfs_stat.len;
431   statbuf = job_m_in.m_lc_vfs_stat.buf;
432 
433   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
434   resolve.l_vmnt_lock = VMNT_READ;
435   resolve.l_vnode_lock = VNODE_READ;
436 
437   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
438   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
439   r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf);
440 
441   unlock_vnode(vp);
442   unlock_vmnt(vmp);
443 
444   put_vnode(vp);
445   return(r);
446 }
447