xref: /minix/minix/servers/vfs/open.c (revision 9f988b79)
1 /* This file contains the procedures for creating, opening, closing, and
2  * seeking on files.
3  *
4  * The entry points into this file are
5  *   do_open:	perform the OPEN system call
6  *   do_mknod:	perform the MKNOD system call
7  *   do_mkdir:	perform the MKDIR system call
8  *   do_close:	perform the CLOSE system call
9  *   do_lseek:  perform the LSEEK system call
10  */
11 
12 #include "fs.h"
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <minix/callnr.h>
18 #include <minix/com.h>
19 #include <minix/u64.h>
20 #include "file.h"
21 #include "lock.h"
22 #include <sys/dirent.h>
23 #include <assert.h>
24 #include <minix/vfsif.h>
25 #include "vnode.h"
26 #include "vmnt.h"
27 #include "path.h"
28 
29 static char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
30 
31 static struct vnode *new_node(struct lookup *resolve, int oflags,
32 	mode_t bits);
33 static int pipe_open(struct vnode *vp, mode_t bits, int oflags);
34 
35 /*===========================================================================*
36  *				do_open					     *
37  *===========================================================================*/
38 int do_open(void)
39 {
40 /* Perform the open(name, flags) system call with O_CREAT *not* set. */
41   int open_flags;
42   char fullpath[PATH_MAX];
43 
44   open_flags = job_m_in.m_lc_vfs_path.flags;
45 
46   if (open_flags & O_CREAT)
47 	return EINVAL;
48 
49   if (copy_path(fullpath, sizeof(fullpath)) != OK)
50 	return(err_code);
51 
52   return common_open(fullpath, open_flags, 0 /*omode*/, FALSE /*for_exec*/);
53 }
54 
55 /*===========================================================================*
56  *				do_creat				     *
57  *===========================================================================*/
58 int do_creat(void)
59 {
60 /* Perform the open(name, flags, mode) system call with O_CREAT set. */
61   int open_flags, create_mode;
62   char fullpath[PATH_MAX];
63   vir_bytes vname;
64   size_t vname_length;
65 
66   vname = job_m_in.m_lc_vfs_creat.name;
67   vname_length = job_m_in.m_lc_vfs_creat.len;
68   open_flags = job_m_in.m_lc_vfs_creat.flags;
69   create_mode = job_m_in.m_lc_vfs_creat.mode;
70 
71   if (!(open_flags & O_CREAT))
72 	return(EINVAL);
73 
74   if (fetch_name(vname, vname_length, fullpath) != OK)
75 	return(err_code);
76 
77   return common_open(fullpath, open_flags, create_mode, FALSE /*for_exec*/);
78 }
79 
80 /*===========================================================================*
81  *				common_open				     *
82  *===========================================================================*/
83 int common_open(char path[PATH_MAX], int oflags, mode_t omode, int for_exec)
84 {
85 /* Common code from do_creat and do_open. */
86   int b, r, exist = TRUE;
87   devmajor_t major_dev;
88   dev_t dev;
89   mode_t bits;
90   struct filp *filp, *filp2;
91   struct vnode *vp;
92   struct vmnt *vmp;
93   struct dmap *dp;
94   struct lookup resolve;
95   int start = 0;
96 
97   /* Remap the bottom two bits of oflags. */
98   bits = (mode_t) mode_map[oflags & O_ACCMODE];
99   if (!bits) return(EINVAL);
100 
101   /* See if file descriptor and filp slots are available. */
102   if ((r = get_fd(fp, start, bits, &fp->fp_fd, &filp)) != OK)
103 	return(r);
104 
105   lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
106 
107   /* If O_CREATE is set, try to make the file. */
108   if (oflags & O_CREAT) {
109         omode = I_REGULAR | (omode & ALLPERMS & fp->fp_umask);
110 	vp = new_node(&resolve, oflags, omode);
111 	r = err_code;
112 	if (r == OK) exist = FALSE;	/* We just created the file */
113 	else if (r != EEXIST) {		/* other error */
114 		if (vp) unlock_vnode(vp);
115 		unlock_filp(filp);
116 		return(r);
117 	}
118 	else exist = !(oflags & O_EXCL);/* file exists, if the O_EXCL
119 					   flag is set this is an error */
120   } else {
121 	/* Scan path name */
122 	resolve.l_vmnt_lock = VMNT_READ;
123 	resolve.l_vnode_lock = VNODE_OPCL;
124 	if ((vp = eat_path(&resolve, fp)) == NULL) {
125 		unlock_filp(filp);
126 		return(err_code);
127 	}
128 
129 	if (vmp != NULL) unlock_vmnt(vmp);
130   }
131 
132   /* Claim the file descriptor and filp slot and fill them in. */
133   fp->fp_filp[fp->fp_fd] = filp;
134   filp->filp_count = 1;
135   filp->filp_vno = vp;
136   filp->filp_flags = oflags;
137   if (oflags & O_CLOEXEC)
138 	FD_SET(fp->fp_fd, &fp->fp_cloexec_set);
139 
140   /* Only do the normal open code if we didn't just create the file. */
141   if (exist) {
142 	/* Check permissions based on the given open flags, except when we are
143 	 * opening an executable for the purpose of passing a file descriptor
144 	 * to its interpreter for execution, in which case we check the X bit.
145 	 */
146 	if ((r = forbidden(fp, vp, for_exec ? X_BIT : bits)) == OK) {
147 		/* Opening reg. files, directories, and special files differ */
148 		switch (vp->v_mode & S_IFMT) {
149 		   case S_IFREG:
150 			/* Truncate regular file if O_TRUNC. */
151 			if (oflags & O_TRUNC) {
152 				if ((r = forbidden(fp, vp, W_BIT)) != OK)
153 					break;
154 				upgrade_vnode_lock(vp);
155 				truncate_vnode(vp, 0);
156 			}
157 			break;
158 		   case S_IFDIR:
159 			/* Directories may be read but not written. */
160 			r = (bits & W_BIT ? EISDIR : OK);
161 			break;
162 		   case S_IFCHR:
163 			/* Invoke the driver for special processing. */
164 			dev = vp->v_sdev;
165 			/* TTY needs to know about the O_NOCTTY flag. */
166 			r = cdev_open(dev, bits | (oflags & O_NOCTTY));
167 			vp = filp->filp_vno;	/* Might be updated by
168 						 * cdev_open after cloning */
169 			break;
170 		   case S_IFBLK:
171 
172 			lock_bsf();
173 
174 			/* Invoke the driver for special processing. */
175 			dev = vp->v_sdev;
176 			r = bdev_open(dev, bits);
177 			if (r != OK) {
178 				unlock_bsf();
179 				break;
180 			}
181 
182 			major_dev = major(vp->v_sdev);
183 			dp = &dmap[major_dev];
184 			if (dp->dmap_driver == NONE) {
185 				printf("VFS: block driver disappeared!\n");
186 				unlock_bsf();
187 				r = ENXIO;
188 				break;
189 			}
190 
191 			/* Check whether the device is mounted or not. If so,
192 			 * then that FS is responsible for this device.
193 			 * Otherwise we default to ROOT_FS.
194 			 */
195 			vp->v_bfs_e = ROOT_FS_E; /* By default */
196 			for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp)
197 				if (vmp->m_dev == vp->v_sdev &&
198 				    !(vmp->m_flags & VMNT_FORCEROOTBSF)) {
199 					vp->v_bfs_e = vmp->m_fs_e;
200 				}
201 
202 			/* Send the driver label to the file system that will
203 			 * handle the block I/O requests (even when its label
204 			 * and endpoint are known already), but only when it is
205 			 * the root file system. Other file systems will
206 			 * already have it anyway.
207 			 */
208 			if (vp->v_bfs_e != ROOT_FS_E) {
209 				unlock_bsf();
210 				break;
211 			}
212 
213 			if (req_newdriver(vp->v_bfs_e, vp->v_sdev,
214 					dp->dmap_label) != OK) {
215 				printf("VFS: error sending driver label\n");
216 				bdev_close(dev);
217 				r = ENXIO;
218 			}
219 			unlock_bsf();
220 			break;
221 
222 		   case S_IFIFO:
223 			/* Create a mapped inode on PFS which handles reads
224 			   and writes to this named pipe. */
225 			upgrade_vnode_lock(vp);
226 			r = map_vnode(vp, PFS_PROC_NR);
227 			if (r == OK) {
228 				if (vp->v_ref_count == 1) {
229 					if (vp->v_size != 0)
230 						r = truncate_vnode(vp, 0);
231 				}
232 				oflags |= O_APPEND;	/* force append mode */
233 				filp->filp_flags = oflags;
234 			}
235 			if (r == OK) {
236 				r = pipe_open(vp, bits, oflags);
237 			}
238 			if (r != ENXIO) {
239 				/* See if someone else is doing a rd or wt on
240 				 * the FIFO.  If so, use its filp entry so the
241 				 * file position will be automatically shared.
242 				 */
243 				b = (bits & R_BIT ? R_BIT : W_BIT);
244 				filp->filp_count = 0; /* don't find self */
245 				if ((filp2 = find_filp(vp, b)) != NULL) {
246 				    /* Co-reader or writer found. Use it.*/
247 				    fp->fp_filp[fp->fp_fd] = filp2;
248 				    filp2->filp_count++;
249 				    filp2->filp_vno = vp;
250 				    filp2->filp_flags = oflags;
251 
252 				    /* v_count was incremented after the vnode
253 				     * has been found. i_count was incremented
254 				     * incorrectly in FS, not knowing that we
255 				     * were going to use an existing filp
256 				     * entry.  Correct this error.
257 				     */
258 				    unlock_vnode(vp);
259 				    put_vnode(vp);
260 				} else {
261 				    /* Nobody else found. Restore filp. */
262 				    filp->filp_count = 1;
263 				}
264 			}
265 			break;
266 		}
267 	}
268   }
269 
270   unlock_filp(filp);
271 
272   /* If error, release inode. */
273   if (r != OK) {
274 	if (r != SUSPEND) {
275 		fp->fp_filp[fp->fp_fd] = NULL;
276 		filp->filp_count = 0;
277 		filp->filp_vno = NULL;
278 		put_vnode(vp);
279 	}
280   } else {
281 	r = fp->fp_fd;
282   }
283 
284   return(r);
285 }
286 
287 
288 /*===========================================================================*
289  *				new_node				     *
290  *===========================================================================*/
291 static struct vnode *new_node(struct lookup *resolve, int oflags, mode_t bits)
292 {
293 /* Try to create a new inode and return a pointer to it. If the inode already
294    exists, return a pointer to it as well, but set err_code accordingly.
295    NULL is returned if the path cannot be resolved up to the last
296    directory, or when the inode cannot be created due to permissions or
297    otherwise. */
298   struct vnode *dirp, *vp;
299   struct vmnt *dir_vmp, *vp_vmp;
300   int r;
301   struct node_details res;
302   struct lookup findnode;
303   char *path;
304 
305   path = resolve->l_path;	/* For easy access */
306 
307   lookup_init(&findnode, path, resolve->l_flags, &dir_vmp, &dirp);
308   findnode.l_vmnt_lock = VMNT_WRITE;
309   findnode.l_vnode_lock = VNODE_WRITE; /* dir node */
310 
311   /* When O_CREAT and O_EXCL flags are set, the path may not be named by a
312    * symbolic link. */
313   if (oflags & O_EXCL) findnode.l_flags |= PATH_RET_SYMLINK;
314 
315   /* See if the path can be opened down to the last directory. */
316   if ((dirp = last_dir(&findnode, fp)) == NULL) return(NULL);
317 
318   /* The final directory is accessible. Get final component of the path. */
319   lookup_init(&findnode, findnode.l_path, findnode.l_flags, &vp_vmp, &vp);
320   findnode.l_vmnt_lock = VMNT_WRITE;
321   findnode.l_vnode_lock = (oflags & O_TRUNC) ? VNODE_WRITE : VNODE_OPCL;
322   vp = advance(dirp, &findnode, fp);
323   assert(vp_vmp == NULL);	/* Lookup to last dir should have yielded lock
324 				 * on vmp or final component does not exist.
325 				 * Either way, vp_vmp ought to be not set.
326 				 */
327 
328   /* The combination of a symlink with absolute path followed by a danglink
329    * symlink results in a new path that needs to be re-resolved entirely. */
330   if (path[0] == '/') {
331 	unlock_vnode(dirp);
332 	unlock_vmnt(dir_vmp);
333 	put_vnode(dirp);
334 	if (vp != NULL) {
335 		unlock_vnode(vp);
336 		put_vnode(vp);
337 	}
338 	return new_node(resolve, oflags, bits);
339   }
340 
341   if (vp == NULL && err_code == ENOENT) {
342 	/* Last path component does not exist. Make a new directory entry. */
343 	if ((vp = get_free_vnode()) == NULL) {
344 		/* Can't create new entry: out of vnodes. */
345 		unlock_vnode(dirp);
346 		unlock_vmnt(dir_vmp);
347 		put_vnode(dirp);
348 		return(NULL);
349 	}
350 
351 	lock_vnode(vp, VNODE_OPCL);
352 	upgrade_vmnt_lock(dir_vmp); /* Creating file, need exclusive access */
353 
354 	if ((r = forbidden(fp, dirp, W_BIT|X_BIT)) != OK ||
355 	    (r = req_create(dirp->v_fs_e, dirp->v_inode_nr,bits, fp->fp_effuid,
356 			    fp->fp_effgid, path, &res)) != OK ) {
357 		/* Can't create inode either due to permissions or some other
358 		 * problem. In case r is EEXIST, we might be dealing with a
359 		 * dangling symlink.*/
360 
361 		/* Downgrade lock to prevent deadlock during symlink resolving*/
362 		downgrade_vmnt_lock(dir_vmp);
363 
364 		if (r == EEXIST) {
365 			struct vnode *slp, *old_wd;
366 
367 
368 			/* Resolve path up to symlink */
369 			findnode.l_flags = PATH_RET_SYMLINK;
370 			findnode.l_vnode_lock = VNODE_READ;
371 			findnode.l_vnode = &slp;
372 			slp = advance(dirp, &findnode, fp);
373 			if (slp != NULL) {
374 				if (S_ISLNK(slp->v_mode)) {
375 					/* Get contents of link */
376 
377 					r = req_rdlink(slp->v_fs_e,
378 						       slp->v_inode_nr,
379 						       VFS_PROC_NR,
380 						       (vir_bytes) path,
381 						       PATH_MAX - 1, 0);
382 					if (r < 0) {
383 						/* Failed to read link */
384 						unlock_vnode(slp);
385 						unlock_vnode(dirp);
386 						unlock_vmnt(dir_vmp);
387 						put_vnode(slp);
388 						put_vnode(dirp);
389 						err_code = r;
390 						return(NULL);
391 					}
392 					path[r] = '\0'; /* Terminate path */
393 				}
394 				unlock_vnode(slp);
395 				put_vnode(slp);
396 			}
397 
398 			/* Try to create the inode the dangling symlink was
399 			 * pointing to. We have to use dirp as starting point
400 			 * as there might be multiple successive symlinks
401 			 * crossing multiple mountpoints.
402 			 * Unlock vnodes and vmnts as we're going to recurse.
403 			 */
404 			unlock_vnode(dirp);
405 			unlock_vnode(vp);
406 			unlock_vmnt(dir_vmp);
407 
408 			old_wd = fp->fp_wd; /* Save orig. working dirp */
409 			fp->fp_wd = dirp;
410 			vp = new_node(resolve, oflags, bits);
411 			fp->fp_wd = old_wd; /* Restore */
412 
413 			if (vp != NULL) {
414 				put_vnode(dirp);
415 				*(resolve->l_vnode) = vp;
416 				return(vp);
417 			}
418 			r = err_code;
419 		}
420 
421 		if (r == EEXIST)
422 			err_code = EIO; /* Impossible, we have verified that
423 					 * the last component doesn't exist and
424 					 * is not a dangling symlink. */
425 		else
426 			err_code = r;
427 
428 		unlock_vmnt(dir_vmp);
429 		unlock_vnode(dirp);
430 		unlock_vnode(vp);
431 		put_vnode(dirp);
432 		return(NULL);
433 	}
434 
435 	/* Store results and mark vnode in use */
436 
437 	vp->v_fs_e = res.fs_e;
438 	vp->v_inode_nr = res.inode_nr;
439 	vp->v_mode = res.fmode;
440 	vp->v_size = res.fsize;
441 	vp->v_uid = res.uid;
442 	vp->v_gid = res.gid;
443 	vp->v_sdev = res.dev;
444 	vp->v_vmnt = dirp->v_vmnt;
445 	vp->v_dev = vp->v_vmnt->m_dev;
446 	vp->v_fs_count = 1;
447 	vp->v_ref_count = 1;
448   } else {
449 	/* Either last component exists, or there is some other problem. */
450 	if (vp != NULL) {
451 		r = EEXIST;	/* File exists or a symlink names a file while
452 				 * O_EXCL is set. */
453 	} else
454 		r = err_code;	/* Other problem. */
455   }
456 
457   err_code = r;
458   /* When dirp equals vp, we shouldn't release the lock as a vp is locked only
459    * once. Releasing the lock would cause the resulting vp not be locked and
460    * cause mayhem later on. */
461   if (dirp != vp) {
462 	unlock_vnode(dirp);
463   }
464   unlock_vmnt(dir_vmp);
465   put_vnode(dirp);
466 
467   *(resolve->l_vnode) = vp;
468   return(vp);
469 }
470 
471 
472 /*===========================================================================*
473  *				pipe_open				     *
474  *===========================================================================*/
475 static int pipe_open(struct vnode *vp, mode_t bits, int oflags)
476 {
477 /*  This function is called from common_open. It checks if
478  *  there is at least one reader/writer pair for the pipe, if not
479  *  it suspends the caller, otherwise it revives all other blocked
480  *  processes hanging on the pipe.
481  */
482 
483   if ((bits & (R_BIT|W_BIT)) == (R_BIT|W_BIT)) return(ENXIO);
484 
485   /* Find the reader/writer at the other end of the pipe */
486   if (find_filp(vp, bits & W_BIT ? R_BIT : W_BIT) == NULL) {
487 	/* Not found */
488 	if (oflags & O_NONBLOCK) {
489 		if (bits & W_BIT) return(ENXIO);
490 	} else {
491 		/* Let's wait for the other side to show up */
492 		suspend(FP_BLOCKED_ON_POPEN);
493 		return(SUSPEND);
494 	}
495   } else if (susp_count > 0) { /* revive blocked processes */
496 	release(vp, VFS_OPEN, susp_count);
497   }
498   return(OK);
499 }
500 
501 
502 /*===========================================================================*
503  *				do_mknod				     *
504  *===========================================================================*/
505 int do_mknod(void)
506 {
507 /* Perform the mknod(name, mode, addr) system call. */
508   register mode_t bits, mode_bits;
509   int r;
510   struct vnode *vp;
511   struct vmnt *vmp;
512   char fullpath[PATH_MAX];
513   struct lookup resolve;
514   vir_bytes vname1;
515   size_t vname1_length;
516   dev_t dev;
517 
518   vname1 = job_m_in.m_lc_vfs_mknod.name;
519   vname1_length = job_m_in.m_lc_vfs_mknod.len;
520   mode_bits = job_m_in.m_lc_vfs_mknod.mode;
521   dev = job_m_in.m_lc_vfs_mknod.device;
522 
523   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
524   resolve.l_vmnt_lock = VMNT_WRITE;
525   resolve.l_vnode_lock = VNODE_WRITE;
526 
527   /* Only the super_user may make nodes other than fifos. */
528   if (!super_user && (!S_ISFIFO(mode_bits) && !S_ISSOCK(mode_bits))) {
529 	return(EPERM);
530   }
531   bits = (mode_bits & S_IFMT) | (mode_bits & ACCESSPERMS & fp->fp_umask);
532 
533   /* Open directory that's going to hold the new node. */
534   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
535   if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
536 
537   /* Make sure that the object is a directory */
538   if (!S_ISDIR(vp->v_mode)) {
539 	r = ENOTDIR;
540   } else if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
541 	r = req_mknod(vp->v_fs_e, vp->v_inode_nr, fullpath, fp->fp_effuid,
542 		      fp->fp_effgid, bits, dev);
543   }
544 
545   unlock_vnode(vp);
546   unlock_vmnt(vmp);
547   put_vnode(vp);
548   return(r);
549 }
550 
551 /*===========================================================================*
552  *				do_mkdir				     *
553  *===========================================================================*/
554 int do_mkdir(void)
555 {
556 /* Perform the mkdir(name, mode) system call. */
557   mode_t bits;			/* mode bits for the new inode */
558   int r;
559   struct vnode *vp;
560   struct vmnt *vmp;
561   char fullpath[PATH_MAX];
562   struct lookup resolve;
563   mode_t dirmode;
564 
565   if (copy_path(fullpath, sizeof(fullpath)) != OK)
566 	return(err_code);
567   dirmode = job_m_in.m_lc_vfs_path.mode;
568 
569   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
570   resolve.l_vmnt_lock = VMNT_WRITE;
571   resolve.l_vnode_lock = VNODE_WRITE;
572 
573   bits = I_DIRECTORY | (dirmode & RWX_MODES & fp->fp_umask);
574   if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
575 
576   /* Make sure that the object is a directory */
577   if (!S_ISDIR(vp->v_mode)) {
578 	r = ENOTDIR;
579   } else if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
580 	r = req_mkdir(vp->v_fs_e, vp->v_inode_nr, fullpath, fp->fp_effuid,
581 		      fp->fp_effgid, bits);
582   }
583 
584   unlock_vnode(vp);
585   unlock_vmnt(vmp);
586   put_vnode(vp);
587   return(r);
588 }
589 
590 /*===========================================================================*
591  *				actual_lseek				     *
592  *===========================================================================*/
593 int actual_lseek(struct fproc *rfp, int seekfd, int seekwhence, off_t offset,
594 	off_t *newposp)
595 {
596   register struct filp *rfilp;
597   int r = OK;
598   off_t pos, newpos;
599 
600   /* Check to see if the file descriptor is valid. */
601   if ( (rfilp = get_filp2(rfp, seekfd, VNODE_READ)) == NULL) {
602 	return(err_code);
603   }
604 
605   /* No lseek on pipes. */
606   if (S_ISFIFO(rfilp->filp_vno->v_mode)) {
607 	unlock_filp(rfilp);
608 	return(ESPIPE);
609   }
610 
611   /* The value of 'whence' determines the start position to use. */
612   switch(seekwhence) {
613     case SEEK_SET: pos = 0; break;
614     case SEEK_CUR: pos = rfilp->filp_pos; break;
615     case SEEK_END: pos = rfilp->filp_vno->v_size; break;
616     default: unlock_filp(rfilp); return(EINVAL);
617   }
618 
619   newpos = pos + offset;
620 
621   /* Check for overflow. */
622   if ((offset > 0) && (newpos <= pos)) {
623 	r = EOVERFLOW;
624   } else if ((offset < 0) && (newpos >= pos)) {
625 	r = EOVERFLOW;
626   } else {
627 	if (newposp != NULL) *newposp = newpos;
628 
629 	if (newpos != rfilp->filp_pos) {
630 		rfilp->filp_pos = newpos;
631 
632 		/* Inhibit read ahead request */
633 		r = req_inhibread(rfilp->filp_vno->v_fs_e,
634 				  rfilp->filp_vno->v_inode_nr);
635 	}
636   }
637 
638   unlock_filp(rfilp);
639   return(r);
640 }
641 
642 /*===========================================================================*
643  *				do_lseek				     *
644  *===========================================================================*/
645 int do_lseek(void)
646 {
647   /* Perform the lseek(2) system call. */
648   off_t newpos;
649   int r;
650 
651   if ((r = actual_lseek(fp, job_m_in.m_lc_vfs_lseek.fd,
652 		job_m_in.m_lc_vfs_lseek.whence, job_m_in.m_lc_vfs_lseek.offset,
653 		&newpos)) != OK)
654 	return r;
655 
656   /* insert the new position into the output message */
657   job_m_out.m_vfs_lc_lseek.offset = newpos;
658   return OK;
659 }
660 
661 /*===========================================================================*
662  *				do_close				     *
663  *===========================================================================*/
664 int do_close(void)
665 {
666 /* Perform the close(fd) system call. */
667   int thefd = job_m_in.m_lc_vfs_close.fd;
668   return close_fd(fp, thefd);
669 }
670 
671 
672 /*===========================================================================*
673  *				close_fd				     *
674  *===========================================================================*/
675 int close_fd(rfp, fd_nr)
676 struct fproc *rfp;
677 int fd_nr;
678 {
679 /* Perform the close(fd) system call. */
680   register struct filp *rfilp;
681   register struct vnode *vp;
682   struct file_lock *flp;
683   int lock_count;
684 
685   /* First locate the vnode that belongs to the file descriptor. */
686   if ( (rfilp = get_filp2(rfp, fd_nr, VNODE_OPCL)) == NULL) return(err_code);
687 
688   vp = rfilp->filp_vno;
689 
690   /* first, make all future get_filp2()'s fail; otherwise
691    * we might try to close the same fd in different threads
692    */
693   rfp->fp_filp[fd_nr] = NULL;
694 
695   close_filp(rfilp);
696 
697   FD_CLR(fd_nr, &rfp->fp_cloexec_set);
698 
699   /* Check to see if the file is locked.  If so, release all locks. */
700   if (nr_locks > 0) {
701 	lock_count = nr_locks;	/* save count of locks */
702 	for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) {
703 		if (flp->lock_type == 0) continue;	/* slot not in use */
704 		if (flp->lock_vnode == vp && flp->lock_pid == rfp->fp_pid) {
705 			flp->lock_type = 0;
706 			nr_locks--;
707 		}
708 	}
709 	if (nr_locks < lock_count)
710 		lock_revive();	/* one or more locks released */
711   }
712 
713   return(OK);
714 }
715