xref: /minix3/minix/servers/vfs/mount.c (revision f859061eafa7665d8241f2f3949ecb2ce461295c)
1433d6423SLionel Sambuc /* This file performs the MOUNT and UMOUNT system calls.
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * The entry points into this file are
4433d6423SLionel Sambuc  *   do_mount:		perform the MOUNT system call
5433d6423SLionel Sambuc  *   do_umount:		perform the UMOUNT system call
6433d6423SLionel Sambuc  *   unmount:		unmount a file system
7433d6423SLionel Sambuc  */
8433d6423SLionel Sambuc 
9433d6423SLionel Sambuc #include "fs.h"
10433d6423SLionel Sambuc #include <fcntl.h>
11433d6423SLionel Sambuc #include <string.h>
12433d6423SLionel Sambuc #include <minix/callnr.h>
13433d6423SLionel Sambuc #include <minix/com.h>
14433d6423SLionel Sambuc #include <minix/const.h>
15433d6423SLionel Sambuc #include <minix/endpoint.h>
16433d6423SLionel Sambuc #include <minix/syslib.h>
17433d6423SLionel Sambuc #include <minix/bitmap.h>
18433d6423SLionel Sambuc #include <minix/ds.h>
19433d6423SLionel Sambuc #include <unistd.h>
20433d6423SLionel Sambuc #include <sys/stat.h>
21433d6423SLionel Sambuc #include <sys/mount.h>
22433d6423SLionel Sambuc #include <sys/dirent.h>
23433d6423SLionel Sambuc #include <assert.h>
24433d6423SLionel Sambuc #include "file.h"
25433d6423SLionel Sambuc #include <minix/vfsif.h>
26433d6423SLionel Sambuc #include "vnode.h"
27433d6423SLionel Sambuc #include "vmnt.h"
28433d6423SLionel Sambuc #include "path.h"
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /* Allow the root to be replaced before the first 'real' mount. */
31433d6423SLionel Sambuc static int have_root = 0;
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc /* Bitmap of in-use "none" pseudo devices. */
34433d6423SLionel Sambuc static bitchunk_t nonedev[BITMAP_CHUNKS(NR_NONEDEVS)] = { 0 };
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc #define alloc_nonedev(dev) SET_BIT(nonedev, minor(dev) - 1)
37433d6423SLionel Sambuc #define free_nonedev(dev) UNSET_BIT(nonedev, minor(dev) - 1)
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc static dev_t name_to_dev(int allow_mountpt, char path[PATH_MAX]);
40433d6423SLionel Sambuc static dev_t find_free_nonedev(void);
41433d6423SLionel Sambuc static void update_bspec(dev_t dev, endpoint_t fs_e, int send_drv_e);
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc /*===========================================================================*
44433d6423SLionel Sambuc  *				update_bspec				     *
45433d6423SLionel Sambuc  *===========================================================================*/
update_bspec(dev_t dev,endpoint_t fs_e,int send_drv_e)46433d6423SLionel Sambuc static void update_bspec(dev_t dev, endpoint_t fs_e, int send_drv_e)
47433d6423SLionel Sambuc {
48433d6423SLionel Sambuc /* Update all block special files for a certain device, to use a new FS endpt
49433d6423SLionel Sambuc  * to route raw block I/O requests through.
50433d6423SLionel Sambuc  */
51433d6423SLionel Sambuc   struct vnode *vp;
52433d6423SLionel Sambuc   struct dmap *dp;
533b468884SDavid van Moolenbroek   devmajor_t major;
543b468884SDavid van Moolenbroek   int r;
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc   for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; ++vp)
57433d6423SLionel Sambuc 	if (vp->v_ref_count > 0 && S_ISBLK(vp->v_mode) && vp->v_sdev == dev) {
58433d6423SLionel Sambuc 		vp->v_bfs_e = fs_e;
59433d6423SLionel Sambuc 		if (send_drv_e) {
60433d6423SLionel Sambuc 			major = major(dev);
61433d6423SLionel Sambuc 			if (major < 0 || major >= NR_DEVICES) {
62433d6423SLionel Sambuc 				/* Can't update for out-of-range major */
63433d6423SLionel Sambuc 				continue;
64433d6423SLionel Sambuc 			}
65433d6423SLionel Sambuc 			dp = &dmap[major(dev)];
66433d6423SLionel Sambuc 			if (dp->dmap_driver == NONE) {
67433d6423SLionel Sambuc 				/* Can't update for vanished driver */
68433d6423SLionel Sambuc 				printf("VFS: can't send new driver label\n");
69433d6423SLionel Sambuc 				continue;
70433d6423SLionel Sambuc 			}
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc 			if ((r = req_newdriver(fs_e, vp->v_sdev,
73433d6423SLionel Sambuc 					dp->dmap_label)) != OK) {
74433d6423SLionel Sambuc 				printf("VFS: Failed to send new driver label"
75433d6423SLionel Sambuc 				       " for moved block special file to %d\n",
76433d6423SLionel Sambuc 				       fs_e);
77433d6423SLionel Sambuc 			}
78433d6423SLionel Sambuc 		}
79433d6423SLionel Sambuc 	}
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc /*===========================================================================*
83433d6423SLionel Sambuc  *                              do_mount                                     *
84433d6423SLionel Sambuc  *===========================================================================*/
do_mount(void)85433d6423SLionel Sambuc int do_mount(void)
86433d6423SLionel Sambuc {
87433d6423SLionel Sambuc /* Perform the mount(name, mfile, mount_flags) system call. */
88433d6423SLionel Sambuc   endpoint_t fs_e;
89433d6423SLionel Sambuc   int r, slot, nodev;
90433d6423SLionel Sambuc   char mount_path[PATH_MAX], mount_dev[PATH_MAX];
91433d6423SLionel Sambuc   char mount_label[LABEL_MAX], mount_type[FSTYPE_MAX];
92433d6423SLionel Sambuc   dev_t dev;
93433d6423SLionel Sambuc   int mflags;
94433d6423SLionel Sambuc   vir_bytes label, type, vname1, vname2;
95433d6423SLionel Sambuc   size_t vname1_length, vname2_length, label_len, type_len;
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc   mflags = job_m_in.m_lc_vfs_mount.flags;
98433d6423SLionel Sambuc   label = job_m_in.m_lc_vfs_mount.label;
99433d6423SLionel Sambuc   label_len = job_m_in.m_lc_vfs_mount.labellen;
100433d6423SLionel Sambuc   vname1 = job_m_in.m_lc_vfs_mount.dev;
101433d6423SLionel Sambuc   vname1_length = job_m_in.m_lc_vfs_mount.devlen;
102433d6423SLionel Sambuc   vname2 = job_m_in.m_lc_vfs_mount.path;
103433d6423SLionel Sambuc   vname2_length = job_m_in.m_lc_vfs_mount.pathlen;
104433d6423SLionel Sambuc   type = job_m_in.m_lc_vfs_mount.type;
105433d6423SLionel Sambuc   type_len = job_m_in.m_lc_vfs_mount.typelen;
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   /* Only the super-user may do MOUNT. */
108433d6423SLionel Sambuc   if (!super_user) return(EPERM);
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc   /* Get the label from the caller, and ask DS for the endpoint of the FS. */
111433d6423SLionel Sambuc   if (label_len > sizeof(mount_label))
112433d6423SLionel Sambuc 	return EINVAL;
113433d6423SLionel Sambuc   r = sys_datacopy_wrapper(who_e, label, SELF, (vir_bytes) mount_label,
114433d6423SLionel Sambuc 	sizeof(mount_label));
115433d6423SLionel Sambuc   if (r != OK) return(r);
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc   mount_label[sizeof(mount_label)-1] = 0;
118433d6423SLionel Sambuc 
119433d6423SLionel Sambuc   r = ds_retrieve_label_endpt(mount_label, &fs_e);
120433d6423SLionel Sambuc   if (r != OK) return(r);
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc   /* Sanity check on process number. */
123433d6423SLionel Sambuc   if (isokendpt(fs_e, &slot) != OK) return(EINVAL);
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc   /* A null string for block special device means don't use a device at all. */
126433d6423SLionel Sambuc   nodev = (vname1_length == 0);
127433d6423SLionel Sambuc   if (!nodev) {
128433d6423SLionel Sambuc 	/* If 'name' is not for a block special file, return error. */
129433d6423SLionel Sambuc 	if (fetch_name(vname1, vname1_length, mount_dev) != OK)
130433d6423SLionel Sambuc 		return(err_code);
131433d6423SLionel Sambuc 	if ((dev = name_to_dev(FALSE /*allow_mountpt*/, mount_dev)) == NO_DEV)
132433d6423SLionel Sambuc 		return(err_code);
133433d6423SLionel Sambuc   } else {
134433d6423SLionel Sambuc 	/* Find a free pseudo-device as substitute for an actual device. */
135433d6423SLionel Sambuc 	if ((dev = find_free_nonedev()) == NO_DEV)
136433d6423SLionel Sambuc 		return(err_code);
137433d6423SLionel Sambuc 	strlcpy(mount_dev, "none", sizeof(mount_dev));
138433d6423SLionel Sambuc   }
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc   /* Fetch the name of the mountpoint */
141433d6423SLionel Sambuc   if (fetch_name(vname2, vname2_length, mount_path) != OK) return(err_code);
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc   /* Fetch the type of the file system. */
144433d6423SLionel Sambuc   if (type_len > sizeof(mount_type)) return(ENAMETOOLONG);
145433d6423SLionel Sambuc   if (fetch_name(type, type_len, mount_type) != OK) return(err_code);
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc   /* Do the actual job */
148433d6423SLionel Sambuc   return mount_fs(dev, mount_dev, mount_path, fs_e, mflags, mount_type,
149433d6423SLionel Sambuc 	mount_label);
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc /*===========================================================================*
154433d6423SLionel Sambuc  *                              mount_fs				     *
155433d6423SLionel Sambuc  *===========================================================================*/
mount_fs(dev_t dev,char mount_dev[PATH_MAX],char mount_path[PATH_MAX],endpoint_t fs_e,int flags,char mount_type[FSTYPE_MAX],char mount_label[LABEL_MAX])156433d6423SLionel Sambuc int mount_fs(
157433d6423SLionel Sambuc dev_t dev,
158433d6423SLionel Sambuc char mount_dev[PATH_MAX],
159433d6423SLionel Sambuc char mount_path[PATH_MAX],
160433d6423SLionel Sambuc endpoint_t fs_e,
161433d6423SLionel Sambuc int flags,
162433d6423SLionel Sambuc char mount_type[FSTYPE_MAX],
163433d6423SLionel Sambuc char mount_label[LABEL_MAX] )
164433d6423SLionel Sambuc {
165433d6423SLionel Sambuc   int i, r = OK, found, isroot, mount_root, slot;
166433d6423SLionel Sambuc   struct fproc *tfp, *rfp;
167433d6423SLionel Sambuc   struct dmap *dp;
168433d6423SLionel Sambuc   struct vnode *root_node, *vp = NULL;
169433d6423SLionel Sambuc   struct vmnt *new_vmp, *parent_vmp;
170433d6423SLionel Sambuc   char *label;
171433d6423SLionel Sambuc   struct node_details res;
172433d6423SLionel Sambuc   struct lookup resolve;
173433d6423SLionel Sambuc   struct statvfs statvfs_buf;
174433d6423SLionel Sambuc   unsigned int fs_flags;
175433d6423SLionel Sambuc 
176433d6423SLionel Sambuc   /* Look up block device driver label when dev is not a pseudo-device */
177433d6423SLionel Sambuc   label = "";
178433d6423SLionel Sambuc   if (!is_nonedev(dev)) {
179433d6423SLionel Sambuc 	/* Get driver process' endpoint */
180433d6423SLionel Sambuc 	dp = &dmap[major(dev)];
181433d6423SLionel Sambuc 	if (dp->dmap_driver == NONE) {
182433d6423SLionel Sambuc 		printf("VFS: no driver for dev %llx\n", dev);
183433d6423SLionel Sambuc 		return(EINVAL);
184433d6423SLionel Sambuc 	}
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc 	label = dp->dmap_label;
187433d6423SLionel Sambuc 	assert(strlen(label) > 0);
188433d6423SLionel Sambuc   }
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc   /* Scan vmnt table to see if dev already mounted. If not, find a free slot.*/
191433d6423SLionel Sambuc   found = FALSE;
192433d6423SLionel Sambuc   for (i = 0; i < NR_MNTS; ++i) {
193433d6423SLionel Sambuc 	if (vmnt[i].m_dev == dev) found = TRUE;
194433d6423SLionel Sambuc   }
195433d6423SLionel Sambuc   if (found) {
196433d6423SLionel Sambuc 	return(EBUSY);
197433d6423SLionel Sambuc   } else if ((new_vmp = get_free_vmnt()) == NULL) {
198433d6423SLionel Sambuc 	return(ENOMEM);
199433d6423SLionel Sambuc   }
200433d6423SLionel Sambuc   if ((r = lock_vmnt(new_vmp, VMNT_EXCL)) != OK) return(r);
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc   strlcpy(new_vmp->m_mount_path, mount_path, PATH_MAX);
203433d6423SLionel Sambuc   strlcpy(new_vmp->m_mount_dev, mount_dev, PATH_MAX);
204433d6423SLionel Sambuc   strlcpy(new_vmp->m_fstype, mount_type, sizeof(new_vmp->m_fstype));
205433d6423SLionel Sambuc   isroot = (strcmp(mount_path, "/") == 0);
206433d6423SLionel Sambuc   mount_root = (isroot && have_root < 2); /* Root can be mounted twice:
207433d6423SLionel Sambuc 					   * 1: ramdisk
208433d6423SLionel Sambuc 					   * 2: boot disk (e.g., harddisk)
209433d6423SLionel Sambuc 					   */
210433d6423SLionel Sambuc 
211433d6423SLionel Sambuc   if (!mount_root) {
212433d6423SLionel Sambuc 	/* Get vnode of mountpoint */
213433d6423SLionel Sambuc 	lookup_init(&resolve, mount_path, PATH_NOFLAGS, &parent_vmp, &vp);
214433d6423SLionel Sambuc 	resolve.l_vmnt_lock = VMNT_EXCL;
215433d6423SLionel Sambuc 	resolve.l_vnode_lock = VNODE_WRITE;
216433d6423SLionel Sambuc 	if ((vp = eat_path(&resolve, fp)) == NULL)
217433d6423SLionel Sambuc 		r = err_code;
218433d6423SLionel Sambuc 	else if (vp->v_ref_count == 1) {
219433d6423SLionel Sambuc 		/*Tell FS on which vnode it is mounted (glue into mount tree)*/
220433d6423SLionel Sambuc 		r = req_mountpoint(vp->v_fs_e, vp->v_inode_nr);
221433d6423SLionel Sambuc 	} else
222433d6423SLionel Sambuc 		r = EBUSY;
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc 	if (vp != NULL)	{
225433d6423SLionel Sambuc 		/* Quickly unlock to allow back calls (from e.g. FUSE) to
226433d6423SLionel Sambuc 		 * relock */
227433d6423SLionel Sambuc 		unlock_vmnt(parent_vmp);
228433d6423SLionel Sambuc 	}
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc 	if (r != OK) {
231433d6423SLionel Sambuc 		if (vp != NULL) {
232433d6423SLionel Sambuc 			unlock_vnode(vp);
233433d6423SLionel Sambuc 			put_vnode(vp);
234433d6423SLionel Sambuc 		}
235433d6423SLionel Sambuc 		unlock_vmnt(new_vmp);
236433d6423SLionel Sambuc 		return(r);
237433d6423SLionel Sambuc 	}
238433d6423SLionel Sambuc   }
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc   /* We'll need a vnode for the root inode */
241433d6423SLionel Sambuc   if ((root_node = get_free_vnode()) == NULL) {
242433d6423SLionel Sambuc 	if (vp != NULL) {
243433d6423SLionel Sambuc 		unlock_vnode(vp);
244433d6423SLionel Sambuc 		put_vnode(vp);
245433d6423SLionel Sambuc 	}
246433d6423SLionel Sambuc 	unlock_vmnt(new_vmp);
247433d6423SLionel Sambuc 	return(err_code);
248433d6423SLionel Sambuc   }
249433d6423SLionel Sambuc   lock_vnode(root_node, VNODE_OPCL);
250433d6423SLionel Sambuc 
251433d6423SLionel Sambuc   /* Record process as a system process */
252433d6423SLionel Sambuc   if (isokendpt(fs_e, &slot) != OK) {
253433d6423SLionel Sambuc 	if (vp != NULL) {
254433d6423SLionel Sambuc 		unlock_vnode(vp);
255433d6423SLionel Sambuc 		put_vnode(vp);
256433d6423SLionel Sambuc 	}
257433d6423SLionel Sambuc 	unlock_vnode(root_node);
258433d6423SLionel Sambuc 	unlock_vmnt(new_vmp);
259433d6423SLionel Sambuc 	return(EINVAL);
260433d6423SLionel Sambuc   }
261433d6423SLionel Sambuc   rfp = &fproc[slot];
262433d6423SLionel Sambuc   rfp->fp_flags |= FP_SRV_PROC;	/* File Servers are also services */
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc   /* Store some essential vmnt data first */
265433d6423SLionel Sambuc   new_vmp->m_fs_e = fs_e;
266433d6423SLionel Sambuc   new_vmp->m_dev = dev;
267433d6423SLionel Sambuc   if (flags & MNT_RDONLY) new_vmp->m_flags |= VMNT_READONLY;
268433d6423SLionel Sambuc   else new_vmp->m_flags &= ~VMNT_READONLY;
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc   /* Tell FS which device to mount */
271433d6423SLionel Sambuc   new_vmp->m_flags |= VMNT_MOUNTING;
272433d6423SLionel Sambuc   r = req_readsuper(new_vmp, label, dev, !!(flags & MNT_RDONLY), isroot, &res,
273433d6423SLionel Sambuc 	&fs_flags);
274433d6423SLionel Sambuc   new_vmp->m_flags &= ~VMNT_MOUNTING;
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc   new_vmp->m_fs_flags = fs_flags;
277433d6423SLionel Sambuc 
278433d6423SLionel Sambuc   /* Fill the statvfs cache with initial values. */
279433d6423SLionel Sambuc   if (r == OK)
280433d6423SLionel Sambuc 	r = update_statvfs(new_vmp, &statvfs_buf);
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc   if (r != OK) {
283433d6423SLionel Sambuc 	mark_vmnt_free(new_vmp);
284433d6423SLionel Sambuc 	unlock_vnode(root_node);
285433d6423SLionel Sambuc 	if (vp != NULL) {
286433d6423SLionel Sambuc 		unlock_vnode(vp);
287433d6423SLionel Sambuc 		put_vnode(vp);
288433d6423SLionel Sambuc 	}
289433d6423SLionel Sambuc 	unlock_vmnt(new_vmp);
290433d6423SLionel Sambuc 	return(r);
291433d6423SLionel Sambuc   }
292433d6423SLionel Sambuc 
293433d6423SLionel Sambuc   lock_bsf();
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc   /* Fill in root node's fields */
296433d6423SLionel Sambuc   root_node->v_fs_e = res.fs_e;
297433d6423SLionel Sambuc   root_node->v_inode_nr = res.inode_nr;
298433d6423SLionel Sambuc   root_node->v_mode = res.fmode;
299433d6423SLionel Sambuc   root_node->v_uid = res.uid;
300433d6423SLionel Sambuc   root_node->v_gid = res.gid;
301433d6423SLionel Sambuc   root_node->v_size = res.fsize;
302433d6423SLionel Sambuc   root_node->v_sdev = NO_DEV;
303433d6423SLionel Sambuc   root_node->v_fs_count = 1;
304433d6423SLionel Sambuc   root_node->v_ref_count = 1;
305433d6423SLionel Sambuc 
306433d6423SLionel Sambuc   /* Root node is indeed on the partition */
307433d6423SLionel Sambuc   root_node->v_vmnt = new_vmp;
308433d6423SLionel Sambuc   root_node->v_dev = new_vmp->m_dev;
309433d6423SLionel Sambuc   if (!(new_vmp->m_fs_flags & RES_THREADED))
310433d6423SLionel Sambuc 	new_vmp->m_comm.c_max_reqs = 1;
311433d6423SLionel Sambuc   else
312433d6423SLionel Sambuc 	new_vmp->m_comm.c_max_reqs = NR_WTHREADS;
313433d6423SLionel Sambuc   new_vmp->m_comm.c_cur_reqs = 0;
314433d6423SLionel Sambuc 
315433d6423SLionel Sambuc   /* No more blocking operations, so we can now report on this file system. */
316433d6423SLionel Sambuc   new_vmp->m_flags |= VMNT_CANSTAT;
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc   if (mount_root) {
319433d6423SLionel Sambuc 	/* Superblock and root node already read.
320433d6423SLionel Sambuc 	 * Nothing else can go wrong. Perform the mount. */
321433d6423SLionel Sambuc 	new_vmp->m_root_node = root_node;
322433d6423SLionel Sambuc 	new_vmp->m_mounted_on = NULL;
323433d6423SLionel Sambuc 	strlcpy(new_vmp->m_label, mount_label, LABEL_MAX);
324433d6423SLionel Sambuc 	if (is_nonedev(dev)) alloc_nonedev(dev);
325433d6423SLionel Sambuc 	update_bspec(dev, fs_e, 0 /* Don't send new driver endpoint */);
326433d6423SLionel Sambuc 
327433d6423SLionel Sambuc 	ROOT_DEV = dev;
328433d6423SLionel Sambuc 	ROOT_FS_E = fs_e;
329433d6423SLionel Sambuc 
330433d6423SLionel Sambuc 	/* Replace all root and working directories */
331433d6423SLionel Sambuc 	for (i = 0, tfp = fproc; i < NR_PROCS; i++, tfp++) {
332433d6423SLionel Sambuc 		if (tfp->fp_pid == PID_FREE)
333433d6423SLionel Sambuc 			continue;
334433d6423SLionel Sambuc 
335433d6423SLionel Sambuc #define		MAKEROOT(what) { 			\
336433d6423SLionel Sambuc 			if (what) put_vnode(what);	\
337433d6423SLionel Sambuc 			dup_vnode(root_node);		\
338433d6423SLionel Sambuc 			what = root_node;		\
339433d6423SLionel Sambuc 		}
340433d6423SLionel Sambuc 
341433d6423SLionel Sambuc 		MAKEROOT(tfp->fp_rd);
342433d6423SLionel Sambuc 		MAKEROOT(tfp->fp_wd);
343433d6423SLionel Sambuc 	}
344433d6423SLionel Sambuc 
345433d6423SLionel Sambuc 	unlock_vnode(root_node);
346433d6423SLionel Sambuc 	unlock_vmnt(new_vmp);
347433d6423SLionel Sambuc 	have_root++; /* We have a (new) root */
348433d6423SLionel Sambuc 	unlock_bsf();
349433d6423SLionel Sambuc 	return(OK);
350433d6423SLionel Sambuc   }
351433d6423SLionel Sambuc 
352433d6423SLionel Sambuc   /* File types may not conflict. */
353433d6423SLionel Sambuc   if (!S_ISDIR(vp->v_mode) && S_ISDIR(root_node->v_mode)) r = EISDIR;
354433d6423SLionel Sambuc 
355433d6423SLionel Sambuc   /* If error, return the super block and both inodes; release the vmnt. */
356433d6423SLionel Sambuc   if (r != OK) {
357433d6423SLionel Sambuc 	unlock_vnode(vp);
358433d6423SLionel Sambuc 	unlock_vnode(root_node);
359433d6423SLionel Sambuc 	mark_vmnt_free(new_vmp);
360433d6423SLionel Sambuc 	unlock_vmnt(new_vmp);
361433d6423SLionel Sambuc 	put_vnode(vp);
362433d6423SLionel Sambuc 	put_vnode(root_node);
363433d6423SLionel Sambuc 	unlock_bsf();
364433d6423SLionel Sambuc 	return(r);
365433d6423SLionel Sambuc   }
366433d6423SLionel Sambuc 
367433d6423SLionel Sambuc   /* Nothing else can go wrong.  Perform the mount. */
368433d6423SLionel Sambuc   new_vmp->m_mounted_on = vp;
369433d6423SLionel Sambuc   new_vmp->m_root_node = root_node;
370433d6423SLionel Sambuc   strlcpy(new_vmp->m_label, mount_label, LABEL_MAX);
371433d6423SLionel Sambuc 
372433d6423SLionel Sambuc   /* Allocate the pseudo device that was found, if not using a real device. */
373433d6423SLionel Sambuc   if (is_nonedev(dev)) alloc_nonedev(dev);
374433d6423SLionel Sambuc 
375433d6423SLionel Sambuc   /* The new FS will handle block I/O requests for its device now. */
376433d6423SLionel Sambuc   if (!(new_vmp->m_flags & VMNT_FORCEROOTBSF))
377433d6423SLionel Sambuc 	update_bspec(dev, fs_e, 0 /* Don't send new driver endpoint */);
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc   unlock_vnode(vp);
380433d6423SLionel Sambuc   unlock_vnode(root_node);
381433d6423SLionel Sambuc   unlock_vmnt(new_vmp);
382433d6423SLionel Sambuc   unlock_bsf();
383433d6423SLionel Sambuc 
384433d6423SLionel Sambuc   return(OK);
385433d6423SLionel Sambuc }
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc 
388433d6423SLionel Sambuc /*===========================================================================*
389433d6423SLionel Sambuc  *				mount_pfs				     *
390433d6423SLionel Sambuc  *===========================================================================*/
mount_pfs(void)391433d6423SLionel Sambuc void mount_pfs(void)
392433d6423SLionel Sambuc {
393*f859061eSDavid van Moolenbroek /* Mount the Pipe File Server.  We treat it as a regular file system to a
394*f859061eSDavid van Moolenbroek  * certain extent, to prevent creating too many exceptions all over the place.
395*f859061eSDavid van Moolenbroek  * For example, it has a vmnt entry to make locking easier, and it gets sent
396*f859061eSDavid van Moolenbroek  * a mount request to keep the fsdriver library happy.
397*f859061eSDavid van Moolenbroek  */
398433d6423SLionel Sambuc   dev_t dev;
399433d6423SLionel Sambuc   struct vmnt *vmp;
400*f859061eSDavid van Moolenbroek   struct node_details res;
401*f859061eSDavid van Moolenbroek   unsigned int fs_flags;
402*f859061eSDavid van Moolenbroek   int r;
403433d6423SLionel Sambuc 
404433d6423SLionel Sambuc   if ((dev = find_free_nonedev()) == NO_DEV)
405433d6423SLionel Sambuc 	panic("VFS: no nonedev to initialize PFS");
406433d6423SLionel Sambuc 
407433d6423SLionel Sambuc   if ((vmp = get_free_vmnt()) == NULL)
408433d6423SLionel Sambuc 	panic("VFS: no vmnt to initialize PFS");
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc   alloc_nonedev(dev);
411433d6423SLionel Sambuc 
412433d6423SLionel Sambuc   vmp->m_dev = dev;
413433d6423SLionel Sambuc   vmp->m_fs_e = PFS_PROC_NR;
414433d6423SLionel Sambuc   vmp->m_fs_flags = 0;
415433d6423SLionel Sambuc   strlcpy(vmp->m_label, "pfs", LABEL_MAX);
416433d6423SLionel Sambuc   strlcpy(vmp->m_mount_path, "pipe", PATH_MAX);
417433d6423SLionel Sambuc   strlcpy(vmp->m_mount_dev, "none", PATH_MAX);
418*f859061eSDavid van Moolenbroek 
419*f859061eSDavid van Moolenbroek   /* Ask PFS to acknowledge being mounted. Ignore the returned node details. */
420*f859061eSDavid van Moolenbroek   r = req_readsuper(vmp, "", dev, FALSE, FALSE, &res, &fs_flags);
421*f859061eSDavid van Moolenbroek   if (r != OK)
422*f859061eSDavid van Moolenbroek 	printf("VFS: unable to mount PFS (%d)\n", r);
423*f859061eSDavid van Moolenbroek   else
424*f859061eSDavid van Moolenbroek 	vmp->m_fs_flags = fs_flags;
425433d6423SLionel Sambuc }
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc /*===========================================================================*
428433d6423SLionel Sambuc  *                              do_umount                                    *
429433d6423SLionel Sambuc  *===========================================================================*/
do_umount(void)430433d6423SLionel Sambuc int do_umount(void)
431433d6423SLionel Sambuc {
432433d6423SLionel Sambuc /* Perform the umount(name) system call.  Return the label of the FS service.
433433d6423SLionel Sambuc  */
434433d6423SLionel Sambuc   char label[LABEL_MAX];
435433d6423SLionel Sambuc   dev_t dev;
436433d6423SLionel Sambuc   int r;
437433d6423SLionel Sambuc   char fullpath[PATH_MAX];
438433d6423SLionel Sambuc   vir_bytes vname, label_addr;
439433d6423SLionel Sambuc   size_t vname_length, label_len;
440433d6423SLionel Sambuc 
441433d6423SLionel Sambuc   vname = job_m_in.m_lc_vfs_umount.name;
442433d6423SLionel Sambuc   vname_length = job_m_in.m_lc_vfs_umount.namelen;
443433d6423SLionel Sambuc   label_addr = job_m_in.m_lc_vfs_umount.label;
444433d6423SLionel Sambuc   label_len = job_m_in.m_lc_vfs_umount.labellen;
445433d6423SLionel Sambuc 
446433d6423SLionel Sambuc   /* Only the super-user may do umount. */
447433d6423SLionel Sambuc   if (!super_user) return(EPERM);
448433d6423SLionel Sambuc 
449433d6423SLionel Sambuc   /* If 'name' is not for a block special file or mountpoint, return error. */
450433d6423SLionel Sambuc   if (fetch_name(vname, vname_length, fullpath) != OK)
451433d6423SLionel Sambuc 	return(err_code);
452433d6423SLionel Sambuc   if ((dev = name_to_dev(TRUE /*allow_mountpt*/, fullpath)) == NO_DEV)
453433d6423SLionel Sambuc 	return(err_code);
454433d6423SLionel Sambuc 
455433d6423SLionel Sambuc   if ((r = unmount(dev, label)) != OK) return(r);
456433d6423SLionel Sambuc 
457433d6423SLionel Sambuc   /* Return the label of the mounted file system, so that the caller
458433d6423SLionel Sambuc    * can shut down the corresponding server process.
459433d6423SLionel Sambuc    */
460433d6423SLionel Sambuc   if (strlen(label) >= label_len)
461433d6423SLionel Sambuc 	label[label_len-1] = 0;
462433d6423SLionel Sambuc   return sys_datacopy_wrapper(SELF, (vir_bytes) label, who_e, label_addr,
463433d6423SLionel Sambuc 	strlen(label) + 1);
464433d6423SLionel Sambuc }
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc 
467433d6423SLionel Sambuc /*===========================================================================*
468433d6423SLionel Sambuc  *                              unmount					     *
469433d6423SLionel Sambuc  *===========================================================================*/
unmount(dev_t dev,char label[LABEL_MAX])470433d6423SLionel Sambuc int unmount(
471433d6423SLionel Sambuc   dev_t dev,			/* block-special device */
472433d6423SLionel Sambuc   char label[LABEL_MAX]		/* buffer to retrieve label, or NULL */
473433d6423SLionel Sambuc )
474433d6423SLionel Sambuc {
475433d6423SLionel Sambuc   struct vnode *vp;
476433d6423SLionel Sambuc   struct vmnt *vmp_i = NULL, *vmp = NULL;
477433d6423SLionel Sambuc   int count, locks, r;
478433d6423SLionel Sambuc 
479433d6423SLionel Sambuc   /* Find vmnt that is to be unmounted */
480433d6423SLionel Sambuc   for (vmp_i = &vmnt[0]; vmp_i < &vmnt[NR_MNTS]; ++vmp_i) {
481433d6423SLionel Sambuc 	  if (vmp_i->m_dev == dev) {
482433d6423SLionel Sambuc 		  if(vmp) panic("device mounted more than once: %llx", dev);
483433d6423SLionel Sambuc 		  vmp = vmp_i;
484433d6423SLionel Sambuc 	  }
485433d6423SLionel Sambuc   }
486433d6423SLionel Sambuc 
487433d6423SLionel Sambuc   /* Did we find the vmnt (i.e., was dev a mounted device)? */
488433d6423SLionel Sambuc   if(!vmp) return(EINVAL);
489433d6423SLionel Sambuc 
490433d6423SLionel Sambuc   if ((r = lock_vmnt(vmp, VMNT_EXCL)) != OK) return(r);
491433d6423SLionel Sambuc 
492433d6423SLionel Sambuc   /* See if the mounted device is busy.  Only 1 vnode using it should be
493433d6423SLionel Sambuc    * open -- the root vnode -- and that inode only 1 time. */
494433d6423SLionel Sambuc   locks = count = 0;
495433d6423SLionel Sambuc   for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; vp++)
496433d6423SLionel Sambuc 	  if (vp->v_ref_count > 0 && vp->v_dev == dev) {
497433d6423SLionel Sambuc 		count += vp->v_ref_count;
498433d6423SLionel Sambuc 		if (is_vnode_locked(vp)) locks++;
499433d6423SLionel Sambuc 	  }
500433d6423SLionel Sambuc 
501433d6423SLionel Sambuc   if (count > 1 || locks > 1 || tll_haspendinglock(&vmp->m_lock)) {
502433d6423SLionel Sambuc 	unlock_vmnt(vmp);
503433d6423SLionel Sambuc 	return(EBUSY);    /* can't umount a busy file system */
504433d6423SLionel Sambuc   }
505433d6423SLionel Sambuc 
506433d6423SLionel Sambuc   /* This FS will now disappear, so stop listing it in statistics. */
507433d6423SLionel Sambuc   vmp->m_flags &= ~VMNT_CANSTAT;
508433d6423SLionel Sambuc 
509433d6423SLionel Sambuc   /* Tell FS to drop all inode references for root inode except 1. */
510433d6423SLionel Sambuc   vnode_clean_refs(vmp->m_root_node);
511433d6423SLionel Sambuc 
512433d6423SLionel Sambuc   if (vmp->m_mounted_on) {
513433d6423SLionel Sambuc 	put_vnode(vmp->m_mounted_on);
514433d6423SLionel Sambuc 	vmp->m_mounted_on = NULL;
515433d6423SLionel Sambuc   }
516433d6423SLionel Sambuc 
517433d6423SLionel Sambuc   vmp->m_comm.c_max_reqs = 1;	/* Force max concurrent reqs to just one, so
518433d6423SLionel Sambuc 				 * we won't send any messages after the
519433d6423SLionel Sambuc 				 * unmount request */
520433d6423SLionel Sambuc 
521433d6423SLionel Sambuc   /* Tell FS to unmount */
522433d6423SLionel Sambuc   if ((r = req_unmount(vmp->m_fs_e)) != OK)              /* Not recoverable. */
523433d6423SLionel Sambuc 	printf("VFS: ignoring failed umount attempt FS endpoint: %d (%d)\n",
524433d6423SLionel Sambuc 	       vmp->m_fs_e, r);
525433d6423SLionel Sambuc 
526433d6423SLionel Sambuc   if (is_nonedev(vmp->m_dev)) free_nonedev(vmp->m_dev);
527433d6423SLionel Sambuc 
528433d6423SLionel Sambuc   if (label != NULL) strlcpy(label, vmp->m_label, LABEL_MAX);
529433d6423SLionel Sambuc 
530433d6423SLionel Sambuc   if (vmp->m_root_node) {	/* PFS lacks a root node */
531433d6423SLionel Sambuc 	vmp->m_root_node->v_ref_count = 0;
532433d6423SLionel Sambuc 	vmp->m_root_node->v_fs_count = 0;
533433d6423SLionel Sambuc 	vmp->m_root_node->v_sdev = NO_DEV;
534433d6423SLionel Sambuc 	vmp->m_root_node = NULL;
535433d6423SLionel Sambuc   }
536433d6423SLionel Sambuc   mark_vmnt_free(vmp);
537433d6423SLionel Sambuc 
538433d6423SLionel Sambuc   unlock_vmnt(vmp);
539433d6423SLionel Sambuc 
540433d6423SLionel Sambuc   /* The root FS will handle block I/O requests for this device now. */
541433d6423SLionel Sambuc   lock_bsf();
542433d6423SLionel Sambuc   update_bspec(dev, ROOT_FS_E, 1 /* send new driver endpoint */);
543433d6423SLionel Sambuc   unlock_bsf();
544433d6423SLionel Sambuc 
545433d6423SLionel Sambuc   return(OK);
546433d6423SLionel Sambuc }
547433d6423SLionel Sambuc 
548433d6423SLionel Sambuc 
549433d6423SLionel Sambuc /*===========================================================================*
550433d6423SLionel Sambuc  *				unmount_all				     *
551433d6423SLionel Sambuc  *===========================================================================*/
unmount_all(int force)552433d6423SLionel Sambuc void unmount_all(int force)
553433d6423SLionel Sambuc {
554433d6423SLionel Sambuc /* Unmount all filesystems.  File systems are mounted on other file systems,
555433d6423SLionel Sambuc  * so you have to pull off the loose bits repeatedly to get it all undone.
556433d6423SLionel Sambuc  */
557433d6423SLionel Sambuc 
558433d6423SLionel Sambuc   int i;
559433d6423SLionel Sambuc   struct vmnt *vmp;
560433d6423SLionel Sambuc 
561433d6423SLionel Sambuc   /* Now unmount the rest */
562433d6423SLionel Sambuc   for (i = 0; i < NR_MNTS; i++) {
563433d6423SLionel Sambuc 	/* Unmount at least one. */
564433d6423SLionel Sambuc 	for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
565433d6423SLionel Sambuc 		if (vmp->m_dev != NO_DEV)
566433d6423SLionel Sambuc 			unmount(vmp->m_dev, NULL);
567433d6423SLionel Sambuc 	}
568433d6423SLionel Sambuc   }
569433d6423SLionel Sambuc 
570433d6423SLionel Sambuc   if (!force) return;
571433d6423SLionel Sambuc 
572433d6423SLionel Sambuc   /* Verify nothing is locked anymore */
573433d6423SLionel Sambuc   check_vnode_locks();
574433d6423SLionel Sambuc   check_vmnt_locks();
575433d6423SLionel Sambuc   check_filp_locks();
576433d6423SLionel Sambuc   check_bsf_lock();
577433d6423SLionel Sambuc 
578433d6423SLionel Sambuc   /* Verify we succesfully unmounted all file systems */
579433d6423SLionel Sambuc   for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
580433d6423SLionel Sambuc 	if (vmp->m_dev != NO_DEV) {
581433d6423SLionel Sambuc 		panic("vmp still mounted: %s %d %llx\n", vmp->m_label,
582433d6423SLionel Sambuc 			vmp->m_fs_e, vmp->m_dev);
583433d6423SLionel Sambuc 	}
584433d6423SLionel Sambuc   }
585433d6423SLionel Sambuc }
586433d6423SLionel Sambuc 
587433d6423SLionel Sambuc /*===========================================================================*
588433d6423SLionel Sambuc  *                              name_to_dev                                  *
589433d6423SLionel Sambuc  *===========================================================================*/
name_to_dev(int allow_mountpt,char path[PATH_MAX])590433d6423SLionel Sambuc static dev_t name_to_dev(int allow_mountpt, char path[PATH_MAX])
591433d6423SLionel Sambuc {
592433d6423SLionel Sambuc /* Convert the block special file in 'user_fullpath' to a device number.
593433d6423SLionel Sambuc  * If the given path is not a block special file, but 'allow_mountpt' is set
594433d6423SLionel Sambuc  * and the path is the root node of a mounted file system, return that device
595433d6423SLionel Sambuc  * number. In all other cases, return NO_DEV and an error code in 'err_code'.
596433d6423SLionel Sambuc  */
597433d6423SLionel Sambuc   dev_t dev;
598433d6423SLionel Sambuc   struct vnode *vp;
599433d6423SLionel Sambuc   struct vmnt *vmp;
600433d6423SLionel Sambuc   struct lookup resolve;
601433d6423SLionel Sambuc 
602433d6423SLionel Sambuc   lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
603433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
604433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_READ;
605433d6423SLionel Sambuc 
606433d6423SLionel Sambuc   /* Request lookup */
607433d6423SLionel Sambuc   if ((vp = eat_path(&resolve, fp)) == NULL) return(NO_DEV);
608433d6423SLionel Sambuc 
609433d6423SLionel Sambuc   if (S_ISBLK(vp->v_mode)) {
610433d6423SLionel Sambuc 	dev = vp->v_sdev;
611433d6423SLionel Sambuc   } else if (allow_mountpt && vp->v_vmnt->m_root_node == vp) {
612433d6423SLionel Sambuc 	dev = vp->v_dev;
613433d6423SLionel Sambuc   } else {
614433d6423SLionel Sambuc 	err_code = ENOTBLK;
615433d6423SLionel Sambuc 	dev = NO_DEV;
616433d6423SLionel Sambuc   }
617433d6423SLionel Sambuc 
618433d6423SLionel Sambuc   unlock_vnode(vp);
619433d6423SLionel Sambuc   unlock_vmnt(vmp);
620433d6423SLionel Sambuc   put_vnode(vp);
621433d6423SLionel Sambuc   return(dev);
622433d6423SLionel Sambuc }
623433d6423SLionel Sambuc 
624433d6423SLionel Sambuc 
625433d6423SLionel Sambuc /*===========================================================================*
626433d6423SLionel Sambuc  *                              is_nonedev				     *
627433d6423SLionel Sambuc  *===========================================================================*/
is_nonedev(dev_t dev)628433d6423SLionel Sambuc int is_nonedev(dev_t dev)
629433d6423SLionel Sambuc {
630433d6423SLionel Sambuc /* Return whether the given device is a "none" pseudo device.
631433d6423SLionel Sambuc  */
632433d6423SLionel Sambuc 
633433d6423SLionel Sambuc   return (major(dev) == NONE_MAJOR &&
634433d6423SLionel Sambuc 	minor(dev) > 0 && minor(dev) <= NR_NONEDEVS);
635433d6423SLionel Sambuc }
636433d6423SLionel Sambuc 
637433d6423SLionel Sambuc 
638433d6423SLionel Sambuc /*===========================================================================*
639433d6423SLionel Sambuc  *                              find_free_nonedev			     *
640433d6423SLionel Sambuc  *===========================================================================*/
find_free_nonedev(void)641433d6423SLionel Sambuc static dev_t find_free_nonedev(void)
642433d6423SLionel Sambuc {
643433d6423SLionel Sambuc /* Find a free "none" pseudo device. Do not allocate it yet.
644433d6423SLionel Sambuc  */
645433d6423SLionel Sambuc   int i;
646433d6423SLionel Sambuc 
647433d6423SLionel Sambuc   for (i = 0; i < NR_NONEDEVS; i++)
648433d6423SLionel Sambuc 	if (!GET_BIT(nonedev, i))
649433d6423SLionel Sambuc 		return makedev(NONE_MAJOR, i + 1);
650433d6423SLionel Sambuc 
651433d6423SLionel Sambuc   err_code = EMFILE;
652433d6423SLionel Sambuc   return NO_DEV;
653433d6423SLionel Sambuc }
654