xref: /minix/minix/servers/vfs/misc.c (revision 0a6a1f1d)
1 /* This file contains a collection of miscellaneous procedures.  Some of them
2  * perform simple system calls.  Some others do a little part of system calls
3  * that are mostly performed by the Memory Manager.
4  *
5  * The entry points into this file are
6  *   do_fcntl:	  perform the FCNTL system call
7  *   do_sync:	  perform the SYNC system call
8  *   do_fsync:	  perform the FSYNC system call
9  *   pm_setsid:	  perform VFS's side of setsid system call
10  *   pm_reboot:	  sync disks and prepare for shutdown
11  *   pm_fork:	  adjust the tables after PM has performed a FORK system call
12  *   do_exec:	  handle files with FD_CLOEXEC on after PM has done an EXEC
13  *   do_exit:	  a process has exited; note that in the tables
14  *   do_set:	  set uid or gid for some process
15  *   do_revive:	  revive a process that was waiting for something (e.g. TTY)
16  *   do_svrctl:	  file system control
17  *   do_getsysinfo:	request copy of FS data structure
18  *   pm_dumpcore: create a core dump
19  */
20 
21 #include "fs.h"
22 #include <fcntl.h>
23 #include <assert.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <minix/callnr.h>
27 #include <minix/safecopies.h>
28 #include <minix/endpoint.h>
29 #include <minix/com.h>
30 #include <minix/sysinfo.h>
31 #include <minix/u64.h>
32 #include <sys/ptrace.h>
33 #include <sys/svrctl.h>
34 #include <sys/resource.h>
35 #include "file.h"
36 #include <minix/vfsif.h>
37 #include "vnode.h"
38 #include "vmnt.h"
39 
40 #define CORE_NAME	"core"
41 #define CORE_MODE	0777	/* mode to use on core image files */
42 
43 #if ENABLE_SYSCALL_STATS
44 unsigned long calls_stats[NR_VFS_CALLS];
45 #endif
46 
47 static void free_proc(int flags);
48 
49 /*===========================================================================*
50  *				do_getsysinfo				     *
51  *===========================================================================*/
52 int do_getsysinfo(void)
53 {
54   vir_bytes src_addr, dst_addr;
55   size_t len, buf_size;
56   int what;
57 
58   what = job_m_in.m_lsys_getsysinfo.what;
59   dst_addr = job_m_in.m_lsys_getsysinfo.where;
60   buf_size = job_m_in.m_lsys_getsysinfo.size;
61 
62   /* Only su may call do_getsysinfo. This call may leak information (and is not
63    * stable enough to be part of the API/ABI). In the future, requests from
64    * non-system processes should be denied.
65    */
66 
67   if (!super_user) return(EPERM);
68 
69   switch(what) {
70     case SI_PROC_TAB:
71 	src_addr = (vir_bytes) fproc;
72 	len = sizeof(struct fproc) * NR_PROCS;
73 	break;
74     case SI_DMAP_TAB:
75 	src_addr = (vir_bytes) dmap;
76 	len = sizeof(struct dmap) * NR_DEVICES;
77 	break;
78 #if ENABLE_SYSCALL_STATS
79     case SI_CALL_STATS:
80 	src_addr = (vir_bytes) calls_stats;
81 	len = sizeof(calls_stats);
82 	break;
83 #endif
84     default:
85 	return(EINVAL);
86   }
87 
88   if (len != buf_size)
89 	return(EINVAL);
90 
91   return sys_datacopy_wrapper(SELF, src_addr, who_e, dst_addr, len);
92 }
93 
94 /*===========================================================================*
95  *				do_fcntl				     *
96  *===========================================================================*/
97 int do_fcntl(void)
98 {
99 /* Perform the fcntl(fd, cmd, ...) system call. */
100 
101   register struct filp *f;
102   int new_fd, fl, r = OK, fcntl_req, fcntl_argx;
103   tll_access_t locktype;
104 
105   fp->fp_fd = job_m_in.m_lc_vfs_fcntl.fd;
106   fp->fp_io_buffer = job_m_in.m_lc_vfs_fcntl.arg_ptr;
107   fp->fp_io_nbytes = job_m_in.m_lc_vfs_fcntl.cmd;
108   fcntl_req = job_m_in.m_lc_vfs_fcntl.cmd;
109   fcntl_argx = job_m_in.m_lc_vfs_fcntl.arg_int;
110 
111   /* Is the file descriptor valid? */
112   locktype = (fcntl_req == F_FREESP) ? VNODE_WRITE : VNODE_READ;
113   if ((f = get_filp(fp->fp_fd, locktype)) == NULL)
114 	return(err_code);
115 
116   switch (fcntl_req) {
117     case F_DUPFD:
118     case F_DUPFD_CLOEXEC:
119 	/* This replaces the old dup() system call. */
120 	if (fcntl_argx < 0 || fcntl_argx >= OPEN_MAX) r = EINVAL;
121 	else if ((r = get_fd(fp, fcntl_argx, 0, &new_fd, NULL)) == OK) {
122 		f->filp_count++;
123 		fp->fp_filp[new_fd] = f;
124 		assert(!FD_ISSET(new_fd, &fp->fp_cloexec_set));
125 		if (fcntl_req == F_DUPFD_CLOEXEC)
126 			FD_SET(new_fd, &fp->fp_cloexec_set);
127 		r = new_fd;
128 	}
129 	break;
130 
131     case F_GETFD:
132 	/* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */
133 	r = 0;
134 	if (FD_ISSET(fp->fp_fd, &fp->fp_cloexec_set))
135 		r = FD_CLOEXEC;
136 	break;
137 
138     case F_SETFD:
139 	/* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */
140 	if (fcntl_argx & FD_CLOEXEC)
141 		FD_SET(fp->fp_fd, &fp->fp_cloexec_set);
142 	else
143 		FD_CLR(fp->fp_fd, &fp->fp_cloexec_set);
144 	break;
145 
146     case F_GETFL:
147 	/* Get file status flags (O_NONBLOCK and O_APPEND). */
148 	fl = f->filp_flags & (O_NONBLOCK | O_APPEND | O_ACCMODE);
149 	r = fl;
150 	break;
151 
152     case F_SETFL:
153 	/* Set file status flags (O_NONBLOCK and O_APPEND). */
154 	fl = O_NONBLOCK | O_APPEND;
155 	f->filp_flags = (f->filp_flags & ~fl) | (fcntl_argx & fl);
156 	break;
157 
158     case F_GETLK:
159     case F_SETLK:
160     case F_SETLKW:
161 	/* Set or clear a file lock. */
162 	r = lock_op(f, fcntl_req);
163 	break;
164 
165     case F_FREESP:
166      {
167 	/* Free a section of a file */
168 	off_t start, end, offset;
169 	struct flock flock_arg;
170 
171 	/* Check if it's a regular file. */
172 	if (!S_ISREG(f->filp_vno->v_mode)) r = EINVAL;
173 	else if (!(f->filp_mode & W_BIT)) r = EBADF;
174 	else {
175 		/* Copy flock data from userspace. */
176 		r = sys_datacopy_wrapper(who_e, fp->fp_io_buffer,
177 			SELF, (vir_bytes) &flock_arg, sizeof(flock_arg));
178 	}
179 
180 	if (r != OK) break;
181 
182 	/* Convert starting offset to signed. */
183 	offset = (off_t) flock_arg.l_start;
184 
185 	/* Figure out starting position base. */
186 	switch(flock_arg.l_whence) {
187 	  case SEEK_SET: start = 0; break;
188 	  case SEEK_CUR: start = f->filp_pos; break;
189 	  case SEEK_END: start = f->filp_vno->v_size; break;
190 	  default: r = EINVAL;
191 	}
192 	if (r != OK) break;
193 
194 	/* Check for overflow or underflow. */
195 	if (offset > 0 && start + offset < start) r = EINVAL;
196 	else if (offset < 0 && start + offset > start) r = EINVAL;
197 	else {
198 		start += offset;
199 		if (start < 0) r = EINVAL;
200 	}
201 	if (r != OK) break;
202 
203 	if (flock_arg.l_len != 0) {
204 		if (start >= f->filp_vno->v_size) r = EINVAL;
205 		else if ((end = start + flock_arg.l_len) <= start) r = EINVAL;
206 		else if (end > f->filp_vno->v_size) end = f->filp_vno->v_size;
207 	} else {
208                 end = 0;
209 	}
210 	if (r != OK) break;
211 
212 	r = req_ftrunc(f->filp_vno->v_fs_e, f->filp_vno->v_inode_nr,start,end);
213 
214 	if (r == OK && flock_arg.l_len == 0)
215 		f->filp_vno->v_size = start;
216 
217 	break;
218      }
219     case F_GETNOSIGPIPE:
220 	r = !!(f->filp_flags & O_NOSIGPIPE);
221 	break;
222     case F_SETNOSIGPIPE:
223 	if (fcntl_argx)
224 		f->filp_flags |= O_NOSIGPIPE;
225 	else
226 		f->filp_flags &= ~O_NOSIGPIPE;
227 	break;
228     case F_FLUSH_FS_CACHE:
229     {
230 	struct vnode *vn = f->filp_vno;
231 	mode_t mode = f->filp_vno->v_mode;
232 	if (!super_user) {
233 		r = EPERM;
234 	} else if (S_ISBLK(mode)) {
235 		/* Block device; flush corresponding device blocks. */
236 		r = req_flush(vn->v_bfs_e, vn->v_sdev);
237 	} else if (S_ISREG(mode) || S_ISDIR(mode)) {
238 		/* Directory or regular file; flush hosting FS blocks. */
239 		r = req_flush(vn->v_fs_e, vn->v_dev);
240 	} else {
241 		/* Remaining cases.. Meaning unclear. */
242 		r = ENODEV;
243 	}
244 	break;
245     }
246     default:
247 	r = EINVAL;
248   }
249 
250   unlock_filp(f);
251   return(r);
252 }
253 
254 /*===========================================================================*
255  *				do_sync					     *
256  *===========================================================================*/
257 int do_sync(void)
258 {
259   struct vmnt *vmp;
260   int r = OK;
261 
262   for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
263 	if ((r = lock_vmnt(vmp, VMNT_READ)) != OK)
264 		break;
265 	if (vmp->m_dev != NO_DEV && vmp->m_fs_e != NONE &&
266 		 vmp->m_root_node != NULL) {
267 		req_sync(vmp->m_fs_e);
268 	}
269 	unlock_vmnt(vmp);
270   }
271 
272   return(r);
273 }
274 
275 /*===========================================================================*
276  *				do_fsync				     *
277  *===========================================================================*/
278 int do_fsync(void)
279 {
280 /* Perform the fsync() system call. */
281   struct filp *rfilp;
282   struct vmnt *vmp;
283   dev_t dev;
284   int r = OK;
285 
286   fp->fp_fd = job_m_in.m_lc_vfs_fsync.fd;
287 
288   if ((rfilp = get_filp(fp->fp_fd, VNODE_READ)) == NULL)
289 	return(err_code);
290 
291   dev = rfilp->filp_vno->v_dev;
292   unlock_filp(rfilp);
293 
294   for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
295 	if (vmp->m_dev != dev) continue;
296 	if ((r = lock_vmnt(vmp, VMNT_READ)) != OK)
297 		break;
298 	if (vmp->m_dev != NO_DEV && vmp->m_dev == dev &&
299 		vmp->m_fs_e != NONE && vmp->m_root_node != NULL) {
300 
301 		req_sync(vmp->m_fs_e);
302 	}
303 	unlock_vmnt(vmp);
304   }
305 
306   return(r);
307 }
308 
309 int dupvm(struct fproc *rfp, int pfd, int *vmfd, struct filp **newfilp)
310 {
311 	int result, procfd;
312 	struct filp *f = NULL;
313 	struct fproc *vmf = fproc_addr(VM_PROC_NR);
314 
315 	*newfilp = NULL;
316 
317 	if ((f = get_filp2(rfp, pfd, VNODE_READ)) == NULL) {
318 		printf("VFS dupvm: get_filp2 failed\n");
319 		return EBADF;
320 	}
321 
322 	if(!(f->filp_vno->v_vmnt->m_fs_flags & RES_HASPEEK)) {
323 		unlock_filp(f);
324 #if 0	/* Noisy diagnostic for mmap() by ld.so */
325 		printf("VFS dupvm: no peek available\n");
326 #endif
327 		return EINVAL;
328 	}
329 
330 	assert(f->filp_vno);
331 	assert(f->filp_vno->v_vmnt);
332 
333 	if (!S_ISREG(f->filp_vno->v_mode) && !S_ISBLK(f->filp_vno->v_mode)) {
334 		printf("VFS: mmap regular/blockdev only; dev 0x%llx ino %llu has mode 0%o\n",
335 			f->filp_vno->v_dev, f->filp_vno->v_inode_nr, f->filp_vno->v_mode);
336 		unlock_filp(f);
337 		return EINVAL;
338 	}
339 
340 	/* get free FD in VM */
341 	if((result=get_fd(vmf, 0, 0, &procfd, NULL)) != OK) {
342 		unlock_filp(f);
343 		printf("VFS dupvm: getfd failed\n");
344 		return result;
345 	}
346 
347 	*vmfd = procfd;
348 
349 	f->filp_count++;
350 	assert(f->filp_count > 0);
351 	vmf->fp_filp[procfd] = f;
352 
353 	*newfilp = f;
354 
355 	return OK;
356 }
357 
358 /*===========================================================================*
359  *				do_vm_call				     *
360  *===========================================================================*/
361 int do_vm_call(void)
362 {
363 /* A call that VM does to VFS.
364  * We must reply with the fixed type VM_VFS_REPLY (and put our result info
365  * in the rest of the message) so VM can tell the difference between a
366  * request from VFS and a reply to this call.
367  */
368 	int req = job_m_in.VFS_VMCALL_REQ;
369 	int req_fd = job_m_in.VFS_VMCALL_FD;
370 	u32_t req_id = job_m_in.VFS_VMCALL_REQID;
371 	endpoint_t ep = job_m_in.VFS_VMCALL_ENDPOINT;
372 	u64_t offset = job_m_in.VFS_VMCALL_OFFSET;
373 	u32_t length = job_m_in.VFS_VMCALL_LENGTH;
374 	int result = OK;
375 	int slot;
376 	struct fproc *rfp, *vmf;
377 	struct filp *f = NULL;
378 	int r;
379 
380 	if(job_m_in.m_source != VM_PROC_NR)
381 		return ENOSYS;
382 
383 	if(isokendpt(ep, &slot) != OK) rfp = NULL;
384 	else rfp = &fproc[slot];
385 
386 	vmf = fproc_addr(VM_PROC_NR);
387 	assert(fp == vmf);
388 	assert(rfp != vmf);
389 
390 	switch(req) {
391 		case VMVFSREQ_FDLOOKUP:
392 		{
393 			int procfd;
394 
395 			/* Lookup fd in referenced process. */
396 
397 			if(!rfp) {
398 				printf("VFS: why isn't ep %d here?!\n", ep);
399 				result = ESRCH;
400 				goto reqdone;
401 			}
402 
403 			if((result = dupvm(rfp, req_fd, &procfd, &f)) != OK) {
404 #if 0   /* Noisy diagnostic for mmap() by ld.so */
405 				printf("vfs: dupvm failed\n");
406 #endif
407 				goto reqdone;
408 			}
409 
410 			if(S_ISBLK(f->filp_vno->v_mode)) {
411 				assert(f->filp_vno->v_sdev != NO_DEV);
412 				job_m_out.VMV_DEV = f->filp_vno->v_sdev;
413 				job_m_out.VMV_INO = VMC_NO_INODE;
414 				job_m_out.VMV_SIZE_PAGES = LONG_MAX;
415 			} else {
416 				job_m_out.VMV_DEV = f->filp_vno->v_dev;
417 				job_m_out.VMV_INO = f->filp_vno->v_inode_nr;
418 				job_m_out.VMV_SIZE_PAGES =
419 					roundup(f->filp_vno->v_size,
420 						PAGE_SIZE)/PAGE_SIZE;
421 			}
422 
423 			job_m_out.VMV_FD = procfd;
424 
425 			result = OK;
426 
427 			break;
428 		}
429 		case VMVFSREQ_FDCLOSE:
430 		{
431 			result = close_fd(fp, req_fd);
432 			if(result != OK) {
433 				printf("VFS: VM fd close for fd %d, %d (%d)\n",
434 					req_fd, fp->fp_endpoint, result);
435 			}
436 			break;
437 		}
438 		case VMVFSREQ_FDIO:
439 		{
440 			result = actual_lseek(fp, req_fd, SEEK_SET, offset,
441 				NULL);
442 
443 			if(result == OK) {
444 				result = actual_read_write_peek(fp, PEEKING,
445 					req_fd, /* vir_bytes */ 0, length);
446 			}
447 
448 			break;
449 		}
450 		default:
451 			panic("VFS: bad request code from VM\n");
452 			break;
453 	}
454 
455 reqdone:
456 	if(f)
457 		unlock_filp(f);
458 
459 	/* fp is VM still. */
460 	assert(fp == vmf);
461 	job_m_out.VMV_ENDPOINT = ep;
462 	job_m_out.VMV_RESULT = result;
463 	job_m_out.VMV_REQID = req_id;
464 
465 	/* Reply asynchronously as VM may not be able to receive
466 	 * an ipc_sendnb() message.
467 	 */
468 	job_m_out.m_type = VM_VFS_REPLY;
469 	r = asynsend3(VM_PROC_NR, &job_m_out, 0);
470 	if(r != OK) printf("VFS: couldn't asynsend3() to VM\n");
471 
472 	/* VFS does not reply any further */
473 	return SUSPEND;
474 }
475 
476 /*===========================================================================*
477  *				pm_reboot				     *
478  *===========================================================================*/
479 void pm_reboot()
480 {
481 /* Perform the VFS side of the reboot call. This call is performed from the PM
482  * process context.
483  */
484   message m_out;
485   int i, r;
486   struct fproc *rfp, *pmfp;
487 
488   pmfp = fp;
489 
490   do_sync();
491 
492   /* Do exit processing for all leftover processes and servers, but don't
493    * actually exit them (if they were really gone, PM will tell us about it).
494    * Skip processes that handle parts of the file system; we first need to give
495    * them the chance to unmount (which should be possible as all normal
496    * processes have no open files anymore).
497    */
498   /* This is the only place where we allow special modification of "fp". The
499    * reboot procedure should really be implemented as a PM message broadcasted
500    * to all processes, so that each process will be shut down cleanly by a
501    * thread operating on its behalf. Doing everything here is simpler, but it
502    * requires an exception to the strict model of having "fp" be the process
503    * that owns the current worker thread.
504    */
505   for (i = 0; i < NR_PROCS; i++) {
506 	rfp = &fproc[i];
507 
508 	/* Don't just free the proc right away, but let it finish what it was
509 	 * doing first */
510 	if (rfp != fp) lock_proc(rfp);
511 	if (rfp->fp_endpoint != NONE && find_vmnt(rfp->fp_endpoint) == NULL) {
512 		worker_set_proc(rfp);	/* temporarily fake process context */
513 		free_proc(0);
514 		worker_set_proc(pmfp);	/* restore original process context */
515 	}
516 	if (rfp != fp) unlock_proc(rfp);
517   }
518 
519   do_sync();
520   unmount_all(0 /* Don't force */);
521 
522   /* Try to exit all processes again including File Servers */
523   for (i = 0; i < NR_PROCS; i++) {
524 	rfp = &fproc[i];
525 
526 	/* Don't just free the proc right away, but let it finish what it was
527 	 * doing first */
528 	if (rfp != fp) lock_proc(rfp);
529 	if (rfp->fp_endpoint != NONE) {
530 		worker_set_proc(rfp);	/* temporarily fake process context */
531 		free_proc(0);
532 		worker_set_proc(pmfp);	/* restore original process context */
533 	}
534 	if (rfp != fp) unlock_proc(rfp);
535   }
536 
537   do_sync();
538   unmount_all(1 /* Force */);
539 
540   /* Reply to PM for synchronization */
541   memset(&m_out, 0, sizeof(m_out));
542 
543   m_out.m_type = VFS_PM_REBOOT_REPLY;
544 
545   if ((r = ipc_send(PM_PROC_NR, &m_out)) != OK)
546 	panic("pm_reboot: ipc_send failed: %d", r);
547 }
548 
549 /*===========================================================================*
550  *				pm_fork					     *
551  *===========================================================================*/
552 void pm_fork(endpoint_t pproc, endpoint_t cproc, pid_t cpid)
553 {
554 /* Perform those aspects of the fork() system call that relate to files.
555  * In particular, let the child inherit its parent's file descriptors.
556  * The parent and child parameters tell who forked off whom. The file
557  * system uses the same slot numbers as the kernel.  Only PM makes this call.
558  */
559 
560   struct fproc *cp, *pp;
561   int i, parentno, childno;
562   mutex_t c_fp_lock;
563 
564   /* Check up-to-dateness of fproc. */
565   okendpt(pproc, &parentno);
566 
567   /* PM gives child endpoint, which implies process slot information.
568    * Don't call isokendpt, because that will verify if the endpoint
569    * number is correct in fproc, which it won't be.
570    */
571   childno = _ENDPOINT_P(cproc);
572   if (childno < 0 || childno >= NR_PROCS)
573 	panic("VFS: bogus child for forking: %d", cproc);
574   if (fproc[childno].fp_pid != PID_FREE)
575 	panic("VFS: forking on top of in-use child: %d", childno);
576 
577   /* Copy the parent's fproc struct to the child. */
578   /* However, the mutex variables belong to a slot and must stay the same. */
579   c_fp_lock = fproc[childno].fp_lock;
580   fproc[childno] = fproc[parentno];
581   fproc[childno].fp_lock = c_fp_lock;
582 
583   /* Increase the counters in the 'filp' table. */
584   cp = &fproc[childno];
585   pp = &fproc[parentno];
586 
587   for (i = 0; i < OPEN_MAX; i++)
588 	if (cp->fp_filp[i] != NULL) cp->fp_filp[i]->filp_count++;
589 
590   /* Fill in new process and endpoint id. */
591   cp->fp_pid = cpid;
592   cp->fp_endpoint = cproc;
593 
594   /* A forking process never has an outstanding grant, as it isn't blocking on
595    * I/O. */
596   if (GRANT_VALID(pp->fp_grant)) {
597 	panic("VFS: fork: pp (endpoint %d) has grant %d\n", pp->fp_endpoint,
598 	       pp->fp_grant);
599   }
600   if (GRANT_VALID(cp->fp_grant)) {
601 	panic("VFS: fork: cp (endpoint %d) has grant %d\n", cp->fp_endpoint,
602 	       cp->fp_grant);
603   }
604 
605   /* A child is not a process leader, not being revived, etc. */
606   cp->fp_flags = FP_NOFLAGS;
607 
608   /* Record the fact that both root and working dir have another user. */
609   if (cp->fp_rd) dup_vnode(cp->fp_rd);
610   if (cp->fp_wd) dup_vnode(cp->fp_wd);
611 }
612 
613 /*===========================================================================*
614  *				free_proc				     *
615  *===========================================================================*/
616 static void free_proc(int flags)
617 {
618   int i;
619   register struct fproc *rfp;
620   register struct filp *rfilp;
621   register struct vnode *vp;
622   dev_t dev;
623 
624   if (fp->fp_endpoint == NONE)
625 	panic("free_proc: already free");
626 
627   if (fp_is_blocked(fp))
628 	unpause();
629 
630   /* Loop on file descriptors, closing any that are open. */
631   for (i = 0; i < OPEN_MAX; i++) {
632 	(void) close_fd(fp, i);
633   }
634 
635   /* Release root and working directories. */
636   if (fp->fp_rd) { put_vnode(fp->fp_rd); fp->fp_rd = NULL; }
637   if (fp->fp_wd) { put_vnode(fp->fp_wd); fp->fp_wd = NULL; }
638 
639   /* The rest of these actions is only done when processes actually exit. */
640   if (!(flags & FP_EXITING)) return;
641 
642   fp->fp_flags |= FP_EXITING;
643 
644   /* Check if any process is SUSPENDed on this driver.
645    * If a driver exits, unmap its entries in the dmap table.
646    * (unmapping has to be done after the first step, because the
647    * dmap table is used in the first step.)
648    */
649   unsuspend_by_endpt(fp->fp_endpoint);
650   dmap_unmap_by_endpt(fp->fp_endpoint);
651 
652   worker_stop_by_endpt(fp->fp_endpoint); /* Unblock waiting threads */
653   vmnt_unmap_by_endpt(fp->fp_endpoint); /* Invalidate open files if this
654 					     * was an active FS */
655 
656   /* If a session leader exits and it has a controlling tty, then revoke
657    * access to its controlling tty from all other processes using it.
658    */
659   if ((fp->fp_flags & FP_SESLDR) && fp->fp_tty != 0) {
660       dev = fp->fp_tty;
661       for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
662 	  if(rfp->fp_pid == PID_FREE) continue;
663           if (rfp->fp_tty == dev) rfp->fp_tty = 0;
664 
665           for (i = 0; i < OPEN_MAX; i++) {
666 		if ((rfilp = rfp->fp_filp[i]) == NULL) continue;
667 		if (rfilp->filp_mode == FILP_CLOSED) continue;
668 		vp = rfilp->filp_vno;
669 		if (!S_ISCHR(vp->v_mode)) continue;
670 		if (vp->v_sdev != dev) continue;
671 		lock_filp(rfilp, VNODE_READ);
672 		(void) cdev_close(dev); /* Ignore any errors. */
673 		/* FIXME: missing select check */
674 		rfilp->filp_mode = FILP_CLOSED;
675 		unlock_filp(rfilp);
676           }
677       }
678   }
679 
680   /* Exit done. Mark slot as free. */
681   fp->fp_endpoint = NONE;
682   fp->fp_pid = PID_FREE;
683   fp->fp_flags = FP_NOFLAGS;
684 }
685 
686 /*===========================================================================*
687  *				pm_exit					     *
688  *===========================================================================*/
689 void pm_exit(void)
690 {
691 /* Perform the file system portion of the exit(status) system call.
692  * This function is called from the context of the exiting process.
693  */
694 
695   free_proc(FP_EXITING);
696 }
697 
698 /*===========================================================================*
699  *				pm_setgid				     *
700  *===========================================================================*/
701 void pm_setgid(proc_e, egid, rgid)
702 endpoint_t proc_e;
703 int egid;
704 int rgid;
705 {
706   register struct fproc *tfp;
707   int slot;
708 
709   okendpt(proc_e, &slot);
710   tfp = &fproc[slot];
711 
712   tfp->fp_effgid =  egid;
713   tfp->fp_realgid = rgid;
714 }
715 
716 
717 /*===========================================================================*
718  *				pm_setgroups				     *
719  *===========================================================================*/
720 void pm_setgroups(proc_e, ngroups, groups)
721 endpoint_t proc_e;
722 int ngroups;
723 gid_t *groups;
724 {
725   struct fproc *rfp;
726   int slot;
727 
728   okendpt(proc_e, &slot);
729   rfp = &fproc[slot];
730   if (ngroups * sizeof(gid_t) > sizeof(rfp->fp_sgroups))
731 	panic("VFS: pm_setgroups: too much data to copy");
732   if (sys_datacopy_wrapper(who_e, (vir_bytes) groups, SELF, (vir_bytes) rfp->fp_sgroups,
733 		   ngroups * sizeof(gid_t)) == OK) {
734 	rfp->fp_ngroups = ngroups;
735   } else
736 	panic("VFS: pm_setgroups: datacopy failed");
737 }
738 
739 
740 /*===========================================================================*
741  *				pm_setuid				     *
742  *===========================================================================*/
743 void pm_setuid(proc_e, euid, ruid)
744 endpoint_t proc_e;
745 int euid;
746 int ruid;
747 {
748   struct fproc *tfp;
749   int slot;
750 
751   okendpt(proc_e, &slot);
752   tfp = &fproc[slot];
753 
754   tfp->fp_effuid =  euid;
755   tfp->fp_realuid = ruid;
756 }
757 
758 /*===========================================================================*
759  *				pm_setsid				     *
760  *===========================================================================*/
761 void pm_setsid(endpoint_t proc_e)
762 {
763 /* Perform the VFS side of the SETSID call, i.e. get rid of the controlling
764  * terminal of a process, and make the process a session leader.
765  */
766   struct fproc *rfp;
767   int slot;
768 
769   /* Make the process a session leader with no controlling tty. */
770   okendpt(proc_e, &slot);
771   rfp = &fproc[slot];
772   rfp->fp_flags |= FP_SESLDR;
773   rfp->fp_tty = 0;
774 }
775 
776 /*===========================================================================*
777  *				do_svrctl				     *
778  *===========================================================================*/
779 int do_svrctl(void)
780 {
781   unsigned long svrctl;
782   vir_bytes ptr;
783 
784   svrctl = job_m_in.m_lc_svrctl.request;
785   ptr = job_m_in.m_lc_svrctl.arg;
786 
787   if (IOCGROUP(svrctl) != 'F') return(EINVAL);
788 
789   switch (svrctl) {
790     case VFSSETPARAM:
791     case VFSGETPARAM:
792 	{
793 		struct sysgetenv sysgetenv;
794 		char search_key[64];
795 		char val[64];
796 		int r, s;
797 
798 		/* Copy sysgetenv structure to VFS */
799 		if (sys_datacopy_wrapper(who_e, ptr, SELF, (vir_bytes) &sysgetenv,
800 				 sizeof(sysgetenv)) != OK)
801 			return(EFAULT);
802 
803 		/* Basic sanity checking */
804 		if (svrctl == VFSSETPARAM) {
805 			if (sysgetenv.keylen <= 0 ||
806 			    sysgetenv.keylen > (sizeof(search_key) - 1) ||
807 			    sysgetenv.vallen <= 0 ||
808 			    sysgetenv.vallen >= sizeof(val)) {
809 				return(EINVAL);
810 			}
811 		}
812 
813 		/* Copy parameter "key" */
814 		if ((s = sys_datacopy_wrapper(who_e, (vir_bytes) sysgetenv.key,
815 				      SELF, (vir_bytes) search_key,
816 				      sysgetenv.keylen)) != OK)
817 			return(s);
818 		search_key[sysgetenv.keylen] = '\0'; /* Limit string */
819 
820 		/* Is it a parameter we know? */
821 		if (svrctl == VFSSETPARAM) {
822 			if (!strcmp(search_key, "verbose")) {
823 				int verbose_val;
824 				if ((s = sys_datacopy_wrapper(who_e,
825 				    (vir_bytes) sysgetenv.val, SELF,
826 				    (vir_bytes) &val, sysgetenv.vallen)) != OK)
827 					return(s);
828 				val[sysgetenv.vallen] = '\0'; /* Limit string */
829 				verbose_val = atoi(val);
830 				if (verbose_val < 0 || verbose_val > 4) {
831 					return(EINVAL);
832 				}
833 				verbose = verbose_val;
834 				r = OK;
835 			} else {
836 				r = ESRCH;
837 			}
838 		} else { /* VFSGETPARAM */
839 			char small_buf[60];
840 
841 			r = ESRCH;
842 			if (!strcmp(search_key, "print_traces")) {
843 				mthread_stacktraces();
844 				sysgetenv.val = 0;
845 				sysgetenv.vallen = 0;
846 				r = OK;
847 			} else if (!strcmp(search_key, "active_threads")) {
848 				int active = NR_WTHREADS - worker_available();
849 				snprintf(small_buf, sizeof(small_buf) - 1,
850 					 "%d", active);
851 				sysgetenv.vallen = strlen(small_buf);
852 				r = OK;
853 			}
854 
855 			if (r == OK) {
856 				if ((s = sys_datacopy_wrapper(SELF,
857 				    (vir_bytes) &sysgetenv, who_e, ptr,
858 				    sizeof(sysgetenv))) != OK)
859 					return(s);
860 				if (sysgetenv.val != 0) {
861 					if ((s = sys_datacopy_wrapper(SELF,
862 					    (vir_bytes) small_buf, who_e,
863 					    (vir_bytes) sysgetenv.val,
864 					    sysgetenv.vallen)) != OK)
865 						return(s);
866 				}
867 			}
868 		}
869 
870 		return(r);
871 	}
872     default:
873 	return(EINVAL);
874   }
875 }
876 
877 /*===========================================================================*
878  *				pm_dumpcore				     *
879  *===========================================================================*/
880 int pm_dumpcore(int csig, vir_bytes exe_name)
881 {
882   int r, core_fd;
883   struct filp *f;
884   char core_path[PATH_MAX];
885   char proc_name[PROC_NAME_LEN];
886 
887   /* If a process is blocked, fp->fp_fd holds the fd it's blocked on. Free it
888    * up for use by common_open(). This step is the reason we cannot use this
889    * function to generate a core dump of a process while it is still running
890    * (i.e., without terminating it), as it changes the state of the process.
891    */
892   if (fp_is_blocked(fp))
893           unpause();
894 
895   /* open core file */
896   snprintf(core_path, PATH_MAX, "%s.%d", CORE_NAME, fp->fp_pid);
897   r = core_fd = common_open(core_path, O_WRONLY | O_CREAT | O_TRUNC,
898 	CORE_MODE, FALSE /*for_exec*/);
899   if (r < 0) goto core_exit;
900 
901   /* get process name */
902   r = sys_datacopy_wrapper(PM_PROC_NR, exe_name, VFS_PROC_NR,
903 	(vir_bytes) proc_name, PROC_NAME_LEN);
904   if (r != OK) goto core_exit;
905   proc_name[PROC_NAME_LEN - 1] = '\0';
906 
907   /* write the core dump */
908   f = get_filp(core_fd, VNODE_WRITE);
909   assert(f != NULL);
910   write_elf_core_file(f, csig, proc_name);
911   unlock_filp(f);
912 
913 core_exit:
914   /* The core file descriptor will be closed as part of the process exit. */
915   free_proc(FP_EXITING);
916 
917   return(r);
918 }
919 
920 /*===========================================================================*
921  *				 ds_event				     *
922  *===========================================================================*/
923 void
924 ds_event(void)
925 {
926   char key[DS_MAX_KEYLEN];
927   char *blkdrv_prefix = "drv.blk.";
928   char *chrdrv_prefix = "drv.chr.";
929   u32_t value;
930   int type, r, is_blk;
931   endpoint_t owner_endpoint;
932 
933   /* Get the event and the owner from DS. */
934   while ((r = ds_check(key, &type, &owner_endpoint)) == OK) {
935 	/* Only check for block and character driver up events. */
936 	if (!strncmp(key, blkdrv_prefix, strlen(blkdrv_prefix))) {
937 		is_blk = TRUE;
938 	} else if (!strncmp(key, chrdrv_prefix, strlen(chrdrv_prefix))) {
939 		is_blk = FALSE;
940 	} else {
941 		continue;
942 	}
943 
944 	if ((r = ds_retrieve_u32(key, &value)) != OK) {
945 		printf("VFS: ds_event: ds_retrieve_u32 failed\n");
946 		break;
947 	}
948 	if (value != DS_DRIVER_UP) continue;
949 
950 	/* Perform up. */
951 	dmap_endpt_up(owner_endpoint, is_blk);
952   }
953 
954   if (r != ENOENT) printf("VFS: ds_event: ds_check failed: %d\n", r);
955 }
956 
957 /* A function to be called on panic(). */
958 void panic_hook(void)
959 {
960   printf("VFS mthread stacktraces:\n");
961   mthread_stacktraces();
962 }
963 
964 /*===========================================================================*
965  *				do_getrusage				     *
966  *===========================================================================*/
967 int do_getrusage(void)
968 {
969 	/* Obsolete vfs_getrusage(2) call from userland. The getrusage call is
970 	 * now fully handled by PM, and for any future fields that should be
971 	 * supplied by VFS, VFS should be queried by PM rather than by the user
972 	 * program directly.  TODO: remove this call after the next release.
973 	 */
974 	return OK;
975 }
976