xref: /minix3/minix/servers/vfs/main.c (revision 44707c1900f2a0f1cb5c20e60477b0c2c9ceb595)
1 /*
2  * a loop that gets messages requesting work, carries out the work, and sends
3  * replies.
4  *
5  * The entry points into this file are:
6  *   main:	main program of the Virtual File System
7  *   reply:	send a reply to a process after the requested work is done
8  *
9  */
10 
11 #include "fs.h"
12 #include <fcntl.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <signal.h>
16 #include <assert.h>
17 #include <stdlib.h>
18 #include <sys/ioc_memory.h>
19 #include <sys/svrctl.h>
20 #include <sys/select.h>
21 #include <minix/callnr.h>
22 #include <minix/com.h>
23 #include <minix/const.h>
24 #include <minix/endpoint.h>
25 #include <minix/safecopies.h>
26 #include <minix/debug.h>
27 #include <minix/vfsif.h>
28 #include "file.h"
29 #include "scratchpad.h"
30 #include "vmnt.h"
31 #include "vnode.h"
32 
33 #if ENABLE_SYSCALL_STATS
34 EXTERN unsigned long calls_stats[NR_VFS_CALLS];
35 #endif
36 
37 /* Thread related prototypes */
38 static void do_reply(struct worker_thread *wp);
39 static void do_work(void);
40 static void do_init_root(void);
41 static void handle_work(void (*func)(void));
42 static void reply(message *m_out, endpoint_t whom, int result);
43 
44 static void get_work(void);
45 static void service_pm(void);
46 static int unblock(struct fproc *rfp);
47 
48 /* SEF functions and variables. */
49 static void sef_local_startup(void);
50 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
51 
52 /*===========================================================================*
53  *				main					     *
54  *===========================================================================*/
55 int main(void)
56 {
57 /* This is the main program of the file system.  The main loop consists of
58  * three major activities: getting new work, processing the work, and sending
59  * the reply.  This loop never terminates as long as the file system runs.
60  */
61   int transid;
62   struct worker_thread *wp;
63 
64   /* SEF local startup. */
65   sef_local_startup();
66 
67   printf("Started VFS: %d worker thread(s)\n", NR_WTHREADS);
68 
69   if (OK != (sys_getkinfo(&kinfo)))
70 	panic("couldn't get kernel kinfo");
71 
72   /* This is the main loop that gets work, processes it, and sends replies. */
73   while (TRUE) {
74 	yield_all();	/* let other threads run */
75 	self = NULL;
76 	send_work();
77 	get_work();
78 
79 	transid = TRNS_GET_ID(m_in.m_type);
80 	if (IS_VFS_FS_TRANSID(transid)) {
81 		wp = worker_get((thread_t) transid - VFS_TRANSID);
82 		if (wp == NULL || wp->w_fp == NULL) {
83 			printf("VFS: spurious message %d from endpoint %d\n",
84 				m_in.m_type, m_in.m_source);
85 			continue;
86 		}
87 		m_in.m_type = TRNS_DEL_ID(m_in.m_type);
88 		do_reply(wp);
89 		continue;
90 	} else if (who_e == PM_PROC_NR) { /* Calls from PM */
91 		/* Special control messages from PM */
92 		service_pm();
93 		continue;
94 	} else if (is_notify(call_nr)) {
95 		/* A task ipc_notify()ed us */
96 		switch (who_e) {
97 		case DS_PROC_NR:
98 			/* Start a thread to handle DS events, if no thread
99 			 * is pending or active for it already. DS is not
100 			 * supposed to issue calls to VFS or be the subject of
101 			 * postponed PM requests, so this should be no problem.
102 			 */
103 			if (worker_can_start(fp))
104 				handle_work(ds_event);
105 			break;
106 		case KERNEL:
107 			mthread_stacktraces();
108 			break;
109 		case CLOCK:
110 			/* Timer expired. Used only for select(). Check it. */
111 			expire_timers(m_in.m_notify.timestamp);
112 			break;
113 		default:
114 			printf("VFS: ignoring notification from %d\n", who_e);
115 		}
116 		continue;
117 	} else if (who_p < 0) { /* i.e., message comes from a task */
118 		/* We're going to ignore this message. Tasks should
119 		 * send ipc_notify()s only.
120 		 */
121 		 printf("VFS: ignoring message from %d (%d)\n", who_e, call_nr);
122 		 continue;
123 	}
124 
125 	if (IS_BDEV_RS(call_nr)) {
126 		/* We've got results for a block device request. */
127 		bdev_reply();
128 	} else if (IS_CDEV_RS(call_nr)) {
129 		/* We've got results for a character device request. */
130 		cdev_reply();
131 	} else {
132 		/* Normal syscall. This spawns a new thread. */
133 		handle_work(do_work);
134 	}
135   }
136   return(OK);				/* shouldn't come here */
137 }
138 
139 /*===========================================================================*
140  *			       handle_work				     *
141  *===========================================================================*/
142 static void handle_work(void (*func)(void))
143 {
144 /* Handle asynchronous device replies and new system calls. If the originating
145  * endpoint is an FS endpoint, take extra care not to get in deadlock. */
146   struct vmnt *vmp = NULL;
147   endpoint_t proc_e;
148   int use_spare = FALSE;
149 
150   proc_e = m_in.m_source;
151 
152   if (fp->fp_flags & FP_SRV_PROC) {
153 	vmp = find_vmnt(proc_e);
154 	if (vmp != NULL) {
155 		/* A callback from an FS endpoint. Can do only one at once. */
156 		if (vmp->m_flags & VMNT_CALLBACK) {
157 			replycode(proc_e, EAGAIN);
158 			return;
159 		}
160 		/* Already trying to resolve a deadlock? Can't handle more. */
161 		if (worker_available() == 0) {
162 			replycode(proc_e, EAGAIN);
163 			return;
164 		}
165 		/* A thread is available. Set callback flag. */
166 		vmp->m_flags |= VMNT_CALLBACK;
167 		if (vmp->m_flags & VMNT_MOUNTING) {
168 			vmp->m_flags |= VMNT_FORCEROOTBSF;
169 		}
170 	}
171 
172 	/* Use the spare thread to handle this request if needed. */
173 	use_spare = TRUE;
174   }
175 
176   worker_start(fp, func, &m_in, use_spare);
177 }
178 
179 
180 /*===========================================================================*
181  *			       do_reply				             *
182  *===========================================================================*/
183 static void do_reply(struct worker_thread *wp)
184 {
185   struct vmnt *vmp = NULL;
186 
187   if(who_e != VM_PROC_NR && (vmp = find_vmnt(who_e)) == NULL)
188 	panic("Couldn't find vmnt for endpoint %d", who_e);
189 
190   if (wp->w_task != who_e) {
191 	printf("VFS: tid %d: expected %d to reply, not %d\n",
192 		wp->w_tid, wp->w_task, who_e);
193   }
194   *wp->w_sendrec = m_in;
195   wp->w_task = NONE;
196   if(vmp) vmp->m_comm.c_cur_reqs--; /* We've got our reply, make room for others */
197   worker_signal(wp); /* Continue this thread */
198 }
199 
200 /*===========================================================================*
201  *			       do_pending_pipe				     *
202  *===========================================================================*/
203 static void do_pending_pipe(void)
204 {
205   int r, op;
206   struct filp *f;
207   tll_access_t locktype;
208 
209   f = scratch(fp).file.filp;
210   assert(f != NULL);
211   scratch(fp).file.filp = NULL;
212 
213   locktype = (job_call_nr == VFS_READ) ? VNODE_READ : VNODE_WRITE;
214   op = (job_call_nr == VFS_READ) ? READING : WRITING;
215   lock_filp(f, locktype);
216 
217   r = rw_pipe(op, who_e, f, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes);
218 
219   if (r != SUSPEND) { /* Do we have results to report? */
220 	/* Process is writing, but there is no reader. Send a SIGPIPE signal.
221 	 * This should match the corresponding code in read_write().
222 	 */
223 	if (r == EPIPE && op == WRITING) {
224 		if (!(f->filp_flags & O_NOSIGPIPE))
225 			sys_kill(fp->fp_endpoint, SIGPIPE);
226 	}
227 
228 	replycode(fp->fp_endpoint, r);
229   }
230 
231   unlock_filp(f);
232 }
233 
234 /*===========================================================================*
235  *			       do_work					     *
236  *===========================================================================*/
237 static void do_work(void)
238 {
239   unsigned int call_index;
240   int error;
241 
242   if (fp->fp_pid == PID_FREE) {
243 	/* Process vanished before we were able to handle request.
244 	 * Replying has no use. Just drop it.
245 	 */
246 	return;
247   }
248 
249   memset(&job_m_out, 0, sizeof(job_m_out));
250 
251   /* At this point we assume that we're dealing with a call that has been
252    * made specifically to VFS. Typically it will be a POSIX call from a
253    * normal process, but we also handle a few calls made by drivers such
254    * such as UDS and VND through here. Call the internal function that
255    * does the work.
256    */
257   if (IS_VFS_CALL(job_call_nr)) {
258 	call_index = (unsigned int) (job_call_nr - VFS_BASE);
259 
260 	if (call_index < NR_VFS_CALLS && call_vec[call_index] != NULL) {
261 #if ENABLE_SYSCALL_STATS
262 		calls_stats[call_index]++;
263 #endif
264 		error = (*call_vec[call_index])();
265 	} else
266 		error = ENOSYS;
267   } else
268 	error = ENOSYS;
269 
270   /* Copy the results back to the user and send reply. */
271   if (error != SUSPEND) reply(&job_m_out, fp->fp_endpoint, error);
272 }
273 
274 /*===========================================================================*
275  *			       sef_local_startup			     *
276  *===========================================================================*/
277 static void sef_local_startup()
278 {
279   /* Register init callbacks. */
280   sef_setcb_init_fresh(sef_cb_init_fresh);
281   sef_setcb_init_restart(sef_cb_init_fail);
282 
283   /* No live update support for now. */
284 
285   /* Let SEF perform startup. */
286   sef_startup();
287 }
288 
289 /*===========================================================================*
290  *				sef_cb_init_fresh			     *
291  *===========================================================================*/
292 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info)
293 {
294 /* Initialize the virtual file server. */
295   int s, i;
296   struct fproc *rfp;
297   message mess;
298   struct rprocpub rprocpub[NR_BOOT_PROCS];
299 
300   self = NULL;
301   verbose = 0;
302 
303   /* Initialize proc endpoints to NONE */
304   for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
305 	rfp->fp_endpoint = NONE;
306 	rfp->fp_pid = PID_FREE;
307   }
308 
309   /* Initialize the process table with help of the process manager messages.
310    * Expect one message for each system process with its slot number and pid.
311    * When no more processes follow, the magic process number NONE is sent.
312    * Then, stop and synchronize with the PM.
313    */
314   do {
315 	if ((s = sef_receive(PM_PROC_NR, &mess)) != OK)
316 		panic("VFS: couldn't receive from PM: %d", s);
317 
318 	if (mess.m_type != VFS_PM_INIT)
319 		panic("unexpected message from PM: %d", mess.m_type);
320 
321 	if (NONE == mess.VFS_PM_ENDPT) break;
322 
323 	rfp = &fproc[mess.VFS_PM_SLOT];
324 	rfp->fp_flags = FP_NOFLAGS;
325 	rfp->fp_pid = mess.VFS_PM_PID;
326 	rfp->fp_endpoint = mess.VFS_PM_ENDPT;
327 	rfp->fp_grant = GRANT_INVALID;
328 	rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
329 	rfp->fp_realuid = (uid_t) SYS_UID;
330 	rfp->fp_effuid = (uid_t) SYS_UID;
331 	rfp->fp_realgid = (gid_t) SYS_GID;
332 	rfp->fp_effgid = (gid_t) SYS_GID;
333 	rfp->fp_umask = ~0;
334   } while (TRUE);			/* continue until process NONE */
335   mess.m_type = OK;			/* tell PM that we succeeded */
336   s = ipc_send(PM_PROC_NR, &mess);		/* send synchronization message */
337 
338   system_hz = sys_hz();
339 
340   /* Subscribe to block and character driver events. */
341   s = ds_subscribe("drv\\.[bc]..\\..*", DSF_INITIAL | DSF_OVERWRITE);
342   if (s != OK) panic("VFS: can't subscribe to driver events (%d)", s);
343 
344   /* Initialize worker threads */
345   worker_init();
346 
347   /* Initialize global locks */
348   if (mthread_mutex_init(&bsf_lock, NULL) != 0)
349 	panic("VFS: couldn't initialize block special file lock");
350 
351   init_dmap();			/* Initialize device table. */
352 
353   /* Map all the services in the boot image. */
354   if ((s = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0,
355 			    (vir_bytes) rprocpub, sizeof(rprocpub))) != OK){
356 	panic("sys_safecopyfrom failed: %d", s);
357   }
358   for (i = 0; i < NR_BOOT_PROCS; i++) {
359 	if (rprocpub[i].in_use) {
360 		if ((s = map_service(&rprocpub[i])) != OK) {
361 			panic("VFS: unable to map service: %d", s);
362 		}
363 	}
364   }
365 
366   /* Initialize locks and initial values for all processes. */
367   for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
368 	if (mutex_init(&rfp->fp_lock, NULL) != 0)
369 		panic("unable to initialize fproc lock");
370 	rfp->fp_worker = NULL;
371 #if LOCK_DEBUG
372 	rfp->fp_vp_rdlocks = 0;
373 	rfp->fp_vmnt_rdlocks = 0;
374 #endif
375 
376 	/* Initialize process directories. mount_fs will set them to the
377 	 * correct values.
378 	 */
379 	for (i = 0; i < OPEN_MAX; i++)
380 		rfp->fp_filp[i] = NULL;
381 	rfp->fp_rd = NULL;
382 	rfp->fp_wd = NULL;
383   }
384 
385   init_vnodes();		/* init vnodes */
386   init_vmnts();			/* init vmnt structures */
387   init_select();		/* init select() structures */
388   init_filps();			/* Init filp structures */
389 
390   /* Mount PFS and initial file system root. */
391   worker_start(fproc_addr(VFS_PROC_NR), do_init_root, &mess /*unused*/,
392 	FALSE /*use_spare*/);
393 
394   return(OK);
395 }
396 
397 /*===========================================================================*
398  *			       do_init_root				     *
399  *===========================================================================*/
400 static void do_init_root(void)
401 {
402   char *mount_type, *mount_label;
403   int r;
404 
405   /* Disallow requests from e.g. init(8) while doing the initial mounting. */
406   worker_allow(FALSE);
407 
408   /* Mount the pipe file server. */
409   mount_pfs();
410 
411   /* Mount the root file system. */
412   mount_type = "mfs";       /* FIXME: use boot image process name instead */
413   mount_label = "fs_imgrd"; /* FIXME: obtain this from RS */
414 
415   r = mount_fs(DEV_IMGRD, "bootramdisk", "/", MFS_PROC_NR, 0, mount_type,
416 	mount_label);
417   if (r != OK)
418 	panic("Failed to initialize root");
419 
420   /* All done with mounting, allow requests now. */
421   worker_allow(TRUE);
422 }
423 
424 /*===========================================================================*
425  *				lock_proc				     *
426  *===========================================================================*/
427 void lock_proc(struct fproc *rfp)
428 {
429   int r;
430   struct worker_thread *org_self;
431 
432   r = mutex_trylock(&rfp->fp_lock);
433   if (r == 0) return;
434 
435   org_self = worker_suspend();
436 
437   if ((r = mutex_lock(&rfp->fp_lock)) != 0)
438 	panic("unable to lock fproc lock: %d", r);
439 
440   worker_resume(org_self);
441 }
442 
443 /*===========================================================================*
444  *				unlock_proc				     *
445  *===========================================================================*/
446 void unlock_proc(struct fproc *rfp)
447 {
448   int r;
449 
450   if ((r = mutex_unlock(&rfp->fp_lock)) != 0)
451 	panic("Failed to unlock: %d", r);
452 }
453 
454 /*===========================================================================*
455  *				thread_cleanup				     *
456  *===========================================================================*/
457 void thread_cleanup(void)
458 {
459 /* Perform cleanup actions for a worker thread. */
460 
461 #if LOCK_DEBUG
462   check_filp_locks_by_me();
463   check_vnode_locks_by_me(fp);
464   check_vmnt_locks_by_me(fp);
465 #endif
466 
467   if (fp->fp_flags & FP_SRV_PROC) {
468 	struct vmnt *vmp;
469 
470 	if ((vmp = find_vmnt(fp->fp_endpoint)) != NULL) {
471 		vmp->m_flags &= ~VMNT_CALLBACK;
472 	}
473   }
474 }
475 
476 /*===========================================================================*
477  *				get_work				     *
478  *===========================================================================*/
479 static void get_work()
480 {
481   /* Normally wait for new input.  However, if 'reviving' is
482    * nonzero, a suspended process must be awakened.
483    */
484   int r, found_one, proc_p;
485   register struct fproc *rp;
486 
487   while (reviving != 0) {
488 	found_one = FALSE;
489 
490 	/* Find a suspended process. */
491 	for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++)
492 		if (rp->fp_pid != PID_FREE && (rp->fp_flags & FP_REVIVED)) {
493 			found_one = TRUE; /* Found a suspended process */
494 			if (unblock(rp))
495 				return;	/* So main loop can process job */
496 			send_work();
497 		}
498 
499 	if (!found_one)	/* Consistency error */
500 		panic("VFS: get_work couldn't revive anyone");
501   }
502 
503   for(;;) {
504 	/* Normal case.  No one to revive. Get a useful request. */
505 	if ((r = sef_receive(ANY, &m_in)) != OK) {
506 		panic("VFS: sef_receive error: %d", r);
507 	}
508 
509 	proc_p = _ENDPOINT_P(m_in.m_source);
510 	if (proc_p < 0 || proc_p >= NR_PROCS) fp = NULL;
511 	else fp = &fproc[proc_p];
512 
513 	if (m_in.m_type == EDEADSRCDST) {
514 		printf("VFS: failed ipc_sendrec\n");
515 		return;	/* Failed 'ipc_sendrec' */
516 	}
517 
518 	/* Negative who_p is never used to access the fproc array. Negative
519 	 * numbers (kernel tasks) are treated in a special way.
520 	 */
521 	if (fp && fp->fp_endpoint == NONE) {
522 		printf("VFS: ignoring request from %d: NONE endpoint %d (%d)\n",
523 			m_in.m_source, who_p, m_in.m_type);
524 		continue;
525 	}
526 
527 	/* Internal consistency check; our mental image of process numbers and
528 	 * endpoints must match with how the rest of the system thinks of them.
529 	 */
530 	if (fp && fp->fp_endpoint != who_e) {
531 		if (fproc[who_p].fp_endpoint == NONE)
532 			printf("slot unknown even\n");
533 
534 		panic("VFS: receive endpoint inconsistent (source %d, who_p "
535 			"%d, stored ep %d, who_e %d).\n", m_in.m_source, who_p,
536 			fproc[who_p].fp_endpoint, who_e);
537 	}
538 
539 	return;
540   }
541 }
542 
543 /*===========================================================================*
544  *				reply					     *
545  *===========================================================================*/
546 static void reply(message *m_out, endpoint_t whom, int result)
547 {
548 /* Send a reply to a user process.  If the send fails, just ignore it. */
549   int r;
550 
551   m_out->m_type = result;
552   r = ipc_sendnb(whom, m_out);
553   if (r != OK) {
554 	printf("VFS: %d couldn't send reply %d to %d: %d\n", mthread_self(),
555 		result, whom, r);
556 	util_stacktrace();
557   }
558 }
559 
560 /*===========================================================================*
561  *				replycode				     *
562  *===========================================================================*/
563 void replycode(endpoint_t whom, int result)
564 {
565 /* Send a reply to a user process.  If the send fails, just ignore it. */
566   message m_out;
567 
568   memset(&m_out, 0, sizeof(m_out));
569 
570   reply(&m_out, whom, result);
571 }
572 
573 /*===========================================================================*
574  *				service_pm_postponed			     *
575  *===========================================================================*/
576 void service_pm_postponed(void)
577 {
578   int r, term_signal;
579   vir_bytes core_path;
580   vir_bytes exec_path, stack_frame, pc, newsp, ps_str;
581   size_t exec_path_len, stack_frame_len;
582   endpoint_t proc_e;
583   message m_out;
584 
585   memset(&m_out, 0, sizeof(m_out));
586 
587   switch(job_call_nr) {
588   case VFS_PM_EXEC:
589 	proc_e = job_m_in.VFS_PM_ENDPT;
590 	exec_path = (vir_bytes) job_m_in.VFS_PM_PATH;
591 	exec_path_len = (size_t) job_m_in.VFS_PM_PATH_LEN;
592 	stack_frame = (vir_bytes) job_m_in.VFS_PM_FRAME;
593 	stack_frame_len = (size_t) job_m_in.VFS_PM_FRAME_LEN;
594 	ps_str = (vir_bytes) job_m_in.VFS_PM_PS_STR;
595 
596 	assert(proc_e == fp->fp_endpoint);
597 
598 	r = pm_exec(exec_path, exec_path_len, stack_frame, stack_frame_len,
599 		&pc, &newsp, &ps_str);
600 
601 	/* Reply status to PM */
602 	m_out.m_type = VFS_PM_EXEC_REPLY;
603 	m_out.VFS_PM_ENDPT = proc_e;
604 	m_out.VFS_PM_PC = (void *) pc;
605 	m_out.VFS_PM_STATUS = r;
606 	m_out.VFS_PM_NEWSP = (void *) newsp;
607 	m_out.VFS_PM_NEWPS_STR = ps_str;
608 
609 	break;
610 
611   case VFS_PM_EXIT:
612 	proc_e = job_m_in.VFS_PM_ENDPT;
613 
614 	assert(proc_e == fp->fp_endpoint);
615 
616 	pm_exit();
617 
618 	/* Reply dummy status to PM for synchronization */
619 	m_out.m_type = VFS_PM_EXIT_REPLY;
620 	m_out.VFS_PM_ENDPT = proc_e;
621 
622 	break;
623 
624   case VFS_PM_DUMPCORE:
625 	proc_e = job_m_in.VFS_PM_ENDPT;
626 	term_signal = job_m_in.VFS_PM_TERM_SIG;
627 	core_path = (vir_bytes) job_m_in.VFS_PM_PATH;
628 
629 	assert(proc_e == fp->fp_endpoint);
630 
631 	r = pm_dumpcore(term_signal, core_path);
632 
633 	/* Reply status to PM */
634 	m_out.m_type = VFS_PM_CORE_REPLY;
635 	m_out.VFS_PM_ENDPT = proc_e;
636 	m_out.VFS_PM_STATUS = r;
637 
638 	break;
639 
640   case VFS_PM_UNPAUSE:
641 	proc_e = job_m_in.VFS_PM_ENDPT;
642 
643 	assert(proc_e == fp->fp_endpoint);
644 
645 	unpause();
646 
647 	m_out.m_type = VFS_PM_UNPAUSE_REPLY;
648 	m_out.VFS_PM_ENDPT = proc_e;
649 
650 	break;
651 
652   default:
653 	panic("Unhandled postponed PM call %d", job_m_in.m_type);
654   }
655 
656   r = ipc_send(PM_PROC_NR, &m_out);
657   if (r != OK)
658 	panic("service_pm_postponed: ipc_send failed: %d", r);
659 }
660 
661 /*===========================================================================*
662  *				service_pm				     *
663  *===========================================================================*/
664 static void service_pm(void)
665 {
666 /* Process a request from PM. This function is called from the main thread, and
667  * may therefore not block. Any requests that may require blocking the calling
668  * thread must be executed in a separate thread. Aside from VFS_PM_REBOOT, all
669  * requests from PM involve another, target process: for example, PM tells VFS
670  * that a process is performing a setuid() call. For some requests however,
671  * that other process may not be idle, and in that case VFS must serialize the
672  * PM request handling with any operation is it handling for that target
673  * process. As it happens, the requests that may require blocking are also the
674  * ones where the target process may not be idle. For both these reasons, such
675  * requests are run in worker threads associated to the target process.
676  */
677   struct fproc *rfp;
678   int r, slot;
679   message m_out;
680 
681   memset(&m_out, 0, sizeof(m_out));
682 
683   switch (call_nr) {
684   case VFS_PM_SETUID:
685 	{
686 		endpoint_t proc_e;
687 		uid_t euid, ruid;
688 
689 		proc_e = m_in.VFS_PM_ENDPT;
690 		euid = m_in.VFS_PM_EID;
691 		ruid = m_in.VFS_PM_RID;
692 
693 		pm_setuid(proc_e, euid, ruid);
694 
695 		m_out.m_type = VFS_PM_SETUID_REPLY;
696 		m_out.VFS_PM_ENDPT = proc_e;
697 	}
698 	break;
699 
700   case VFS_PM_SETGID:
701 	{
702 		endpoint_t proc_e;
703 		gid_t egid, rgid;
704 
705 		proc_e = m_in.VFS_PM_ENDPT;
706 		egid = m_in.VFS_PM_EID;
707 		rgid = m_in.VFS_PM_RID;
708 
709 		pm_setgid(proc_e, egid, rgid);
710 
711 		m_out.m_type = VFS_PM_SETGID_REPLY;
712 		m_out.VFS_PM_ENDPT = proc_e;
713 	}
714 	break;
715 
716   case VFS_PM_SETSID:
717 	{
718 		endpoint_t proc_e;
719 
720 		proc_e = m_in.VFS_PM_ENDPT;
721 		pm_setsid(proc_e);
722 
723 		m_out.m_type = VFS_PM_SETSID_REPLY;
724 		m_out.VFS_PM_ENDPT = proc_e;
725 	}
726 	break;
727 
728   case VFS_PM_EXEC:
729   case VFS_PM_EXIT:
730   case VFS_PM_DUMPCORE:
731   case VFS_PM_UNPAUSE:
732 	{
733 		endpoint_t proc_e = m_in.VFS_PM_ENDPT;
734 
735 		if(isokendpt(proc_e, &slot) != OK) {
736 			printf("VFS: proc ep %d not ok\n", proc_e);
737 			return;
738 		}
739 
740 		rfp = &fproc[slot];
741 
742 		/* PM requests on behalf of a proc are handled after the
743 		 * system call that might be in progress for that proc has
744 		 * finished. If the proc is not busy, we start a new thread.
745 		 */
746 		worker_start(rfp, NULL, &m_in, FALSE /*use_spare*/);
747 
748 		return;
749 	}
750   case VFS_PM_FORK:
751   case VFS_PM_SRV_FORK:
752 	{
753 		endpoint_t pproc_e, proc_e;
754 		pid_t child_pid;
755 		uid_t reuid;
756 		gid_t regid;
757 
758 		pproc_e = m_in.VFS_PM_PENDPT;
759 		proc_e = m_in.VFS_PM_ENDPT;
760 		child_pid = m_in.VFS_PM_CPID;
761 		reuid = m_in.VFS_PM_REUID;
762 		regid = m_in.VFS_PM_REGID;
763 
764 		pm_fork(pproc_e, proc_e, child_pid);
765 		m_out.m_type = VFS_PM_FORK_REPLY;
766 
767 		if (call_nr == VFS_PM_SRV_FORK) {
768 			m_out.m_type = VFS_PM_SRV_FORK_REPLY;
769 			pm_setuid(proc_e, reuid, reuid);
770 			pm_setgid(proc_e, regid, regid);
771 		}
772 
773 		m_out.VFS_PM_ENDPT = proc_e;
774 	}
775 	break;
776   case VFS_PM_SETGROUPS:
777 	{
778 		endpoint_t proc_e;
779 		int group_no;
780 		gid_t *group_addr;
781 
782 		proc_e = m_in.VFS_PM_ENDPT;
783 		group_no = m_in.VFS_PM_GROUP_NO;
784 		group_addr = (gid_t *) m_in.VFS_PM_GROUP_ADDR;
785 
786 		pm_setgroups(proc_e, group_no, group_addr);
787 
788 		m_out.m_type = VFS_PM_SETGROUPS_REPLY;
789 		m_out.VFS_PM_ENDPT = proc_e;
790 	}
791 	break;
792 
793   case VFS_PM_REBOOT:
794 	/* Reboot requests are not considered postponed PM work and are instead
795 	 * handled from a separate worker thread that is associated with PM's
796 	 * process. PM makes no regular VFS calls, and thus, from VFS's
797 	 * perspective, PM is always idle. Therefore, we can safely do this.
798 	 * We do assume that PM sends us only one VFS_PM_REBOOT message at
799 	 * once, or ever for that matter. :)
800 	 */
801 	worker_start(fproc_addr(PM_PROC_NR), pm_reboot, &m_in,
802 		FALSE /*use_spare*/);
803 
804 	return;
805 
806     default:
807 	printf("VFS: don't know how to handle PM request %d\n", call_nr);
808 
809 	return;
810   }
811 
812   r = ipc_send(PM_PROC_NR, &m_out);
813   if (r != OK)
814 	panic("service_pm: ipc_send failed: %d", r);
815 }
816 
817 
818 /*===========================================================================*
819  *				unblock					     *
820  *===========================================================================*/
821 static int unblock(rfp)
822 struct fproc *rfp;
823 {
824 /* Unblock a process that was previously blocked on a pipe or a lock.  This is
825  * done by reconstructing the original request and continuing/repeating it.
826  * This function returns TRUE when it has restored a request for execution, and
827  * FALSE if the caller should continue looking for work to do.
828  */
829   int blocked_on;
830 
831   blocked_on = rfp->fp_blocked_on;
832 
833   /* Reconstruct the original request from the saved data. */
834   memset(&m_in, 0, sizeof(m_in));
835   m_in.m_source = rfp->fp_endpoint;
836   m_in.m_type = rfp->fp_block_callnr;
837   switch (m_in.m_type) {
838   case VFS_READ:
839   case VFS_WRITE:
840 	assert(blocked_on == FP_BLOCKED_ON_PIPE);
841 	m_in.m_lc_vfs_readwrite.fd = scratch(rfp).file.fd_nr;
842 	m_in.m_lc_vfs_readwrite.buf = scratch(rfp).io.io_buffer;
843 	m_in.m_lc_vfs_readwrite.len = scratch(rfp).io.io_nbytes;
844 	break;
845   case VFS_FCNTL:
846 	assert(blocked_on == FP_BLOCKED_ON_LOCK);
847 	m_in.m_lc_vfs_fcntl.fd = scratch(rfp).file.fd_nr;
848 	m_in.m_lc_vfs_fcntl.cmd = scratch(rfp).io.io_nbytes;
849 	m_in.m_lc_vfs_fcntl.arg_ptr = scratch(rfp).io.io_buffer;
850 	assert(m_in.m_lc_vfs_fcntl.cmd == F_SETLKW);
851 	break;
852   default:
853 	panic("unblocking call %d blocked on %d ??", m_in.m_type, blocked_on);
854   }
855 
856   rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;	/* no longer blocked */
857   rfp->fp_flags &= ~FP_REVIVED;
858   reviving--;
859   assert(reviving >= 0);
860 
861   /* This should not be device I/O. If it is, it'll 'leak' grants. */
862   assert(!GRANT_VALID(rfp->fp_grant));
863 
864   /* Pending pipe reads/writes cannot be repeated as is, and thus require a
865    * special resumption procedure.
866    */
867   if (blocked_on == FP_BLOCKED_ON_PIPE) {
868 	worker_start(rfp, do_pending_pipe, &m_in, FALSE /*use_spare*/);
869 	return(FALSE);	/* Retrieve more work */
870   }
871 
872   /* A lock request. Repeat the original request as though it just came in. */
873   fp = rfp;
874   return(TRUE);	/* We've unblocked a process */
875 }
876