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