1433d6423SLionel Sambuc /* This file contains the procedures for creating, opening, closing, and
2433d6423SLionel Sambuc * seeking on files.
3433d6423SLionel Sambuc *
4433d6423SLionel Sambuc * The entry points into this file are
5433d6423SLionel Sambuc * do_open: perform the OPEN system call
6433d6423SLionel Sambuc * do_mknod: perform the MKNOD system call
7433d6423SLionel Sambuc * do_mkdir: perform the MKDIR system call
8433d6423SLionel Sambuc * do_close: perform the CLOSE system call
9433d6423SLionel Sambuc * do_lseek: perform the LSEEK system call
10433d6423SLionel Sambuc */
11433d6423SLionel Sambuc
12433d6423SLionel Sambuc #include "fs.h"
13433d6423SLionel Sambuc #include <sys/stat.h>
14433d6423SLionel Sambuc #include <fcntl.h>
15433d6423SLionel Sambuc #include <string.h>
16433d6423SLionel Sambuc #include <unistd.h>
17433d6423SLionel Sambuc #include <minix/callnr.h>
18433d6423SLionel Sambuc #include <minix/com.h>
19433d6423SLionel Sambuc #include <minix/u64.h>
20433d6423SLionel Sambuc #include "file.h"
21433d6423SLionel Sambuc #include "lock.h"
22433d6423SLionel Sambuc #include <sys/dirent.h>
23433d6423SLionel Sambuc #include <assert.h>
24433d6423SLionel Sambuc #include <minix/vfsif.h>
25433d6423SLionel Sambuc #include "vnode.h"
26433d6423SLionel Sambuc #include "vmnt.h"
27433d6423SLionel Sambuc #include "path.h"
28433d6423SLionel Sambuc
29433d6423SLionel Sambuc static char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
30433d6423SLionel Sambuc
31433d6423SLionel Sambuc static struct vnode *new_node(struct lookup *resolve, int oflags,
32433d6423SLionel Sambuc mode_t bits);
33232819ddSDavid van Moolenbroek static int pipe_open(int fd, struct vnode *vp, mode_t bits, int oflags);
34433d6423SLionel Sambuc
35433d6423SLionel Sambuc /*===========================================================================*
36433d6423SLionel Sambuc * do_open *
37433d6423SLionel Sambuc *===========================================================================*/
do_open(void)38433d6423SLionel Sambuc int do_open(void)
39433d6423SLionel Sambuc {
40433d6423SLionel Sambuc /* Perform the open(name, flags) system call with O_CREAT *not* set. */
41433d6423SLionel Sambuc int open_flags;
42433d6423SLionel Sambuc char fullpath[PATH_MAX];
43433d6423SLionel Sambuc
44433d6423SLionel Sambuc open_flags = job_m_in.m_lc_vfs_path.flags;
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc if (open_flags & O_CREAT)
47433d6423SLionel Sambuc return EINVAL;
48433d6423SLionel Sambuc
49433d6423SLionel Sambuc if (copy_path(fullpath, sizeof(fullpath)) != OK)
50433d6423SLionel Sambuc return(err_code);
51433d6423SLionel Sambuc
5256ac45c1SDavid van Moolenbroek return common_open(fullpath, open_flags, 0 /*omode*/, FALSE /*for_exec*/);
53433d6423SLionel Sambuc }
54433d6423SLionel Sambuc
55433d6423SLionel Sambuc /*===========================================================================*
56433d6423SLionel Sambuc * do_creat *
57433d6423SLionel Sambuc *===========================================================================*/
do_creat(void)58433d6423SLionel Sambuc int do_creat(void)
59433d6423SLionel Sambuc {
60433d6423SLionel Sambuc /* Perform the open(name, flags, mode) system call with O_CREAT set. */
61433d6423SLionel Sambuc int open_flags, create_mode;
62433d6423SLionel Sambuc char fullpath[PATH_MAX];
63433d6423SLionel Sambuc vir_bytes vname;
64433d6423SLionel Sambuc size_t vname_length;
65433d6423SLionel Sambuc
66433d6423SLionel Sambuc vname = job_m_in.m_lc_vfs_creat.name;
67433d6423SLionel Sambuc vname_length = job_m_in.m_lc_vfs_creat.len;
68433d6423SLionel Sambuc open_flags = job_m_in.m_lc_vfs_creat.flags;
69433d6423SLionel Sambuc create_mode = job_m_in.m_lc_vfs_creat.mode;
70433d6423SLionel Sambuc
71433d6423SLionel Sambuc if (!(open_flags & O_CREAT))
72433d6423SLionel Sambuc return(EINVAL);
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc if (fetch_name(vname, vname_length, fullpath) != OK)
75433d6423SLionel Sambuc return(err_code);
76433d6423SLionel Sambuc
7756ac45c1SDavid van Moolenbroek return common_open(fullpath, open_flags, create_mode, FALSE /*for_exec*/);
78433d6423SLionel Sambuc }
79433d6423SLionel Sambuc
80433d6423SLionel Sambuc /*===========================================================================*
81433d6423SLionel Sambuc * common_open *
82433d6423SLionel Sambuc *===========================================================================*/
common_open(char path[PATH_MAX],int oflags,mode_t omode,int for_exec)8356ac45c1SDavid van Moolenbroek int common_open(char path[PATH_MAX], int oflags, mode_t omode, int for_exec)
84433d6423SLionel Sambuc {
85433d6423SLionel Sambuc /* Common code from do_creat and do_open. */
863b468884SDavid van Moolenbroek int b, r, exist = TRUE;
873b468884SDavid van Moolenbroek devmajor_t major_dev;
88433d6423SLionel Sambuc dev_t dev;
89433d6423SLionel Sambuc mode_t bits;
90433d6423SLionel Sambuc struct filp *filp, *filp2;
91433d6423SLionel Sambuc struct vnode *vp;
92433d6423SLionel Sambuc struct vmnt *vmp;
93433d6423SLionel Sambuc struct dmap *dp;
94433d6423SLionel Sambuc struct lookup resolve;
95232819ddSDavid van Moolenbroek int fd, start = 0;
96433d6423SLionel Sambuc
97433d6423SLionel Sambuc /* Remap the bottom two bits of oflags. */
98433d6423SLionel Sambuc bits = (mode_t) mode_map[oflags & O_ACCMODE];
99433d6423SLionel Sambuc if (!bits) return(EINVAL);
100433d6423SLionel Sambuc
101433d6423SLionel Sambuc /* See if file descriptor and filp slots are available. */
102232819ddSDavid van Moolenbroek if ((r = get_fd(fp, start, bits, &fd, &filp)) != OK)
103433d6423SLionel Sambuc return(r);
104433d6423SLionel Sambuc
105433d6423SLionel Sambuc lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
106433d6423SLionel Sambuc
107433d6423SLionel Sambuc /* If O_CREATE is set, try to make the file. */
108433d6423SLionel Sambuc if (oflags & O_CREAT) {
109433d6423SLionel Sambuc omode = I_REGULAR | (omode & ALLPERMS & fp->fp_umask);
110433d6423SLionel Sambuc vp = new_node(&resolve, oflags, omode);
111433d6423SLionel Sambuc r = err_code;
112433d6423SLionel Sambuc if (r == OK) exist = FALSE; /* We just created the file */
113433d6423SLionel Sambuc else if (r != EEXIST) { /* other error */
114433d6423SLionel Sambuc if (vp) unlock_vnode(vp);
115433d6423SLionel Sambuc unlock_filp(filp);
116433d6423SLionel Sambuc return(r);
117433d6423SLionel Sambuc }
118433d6423SLionel Sambuc else exist = !(oflags & O_EXCL);/* file exists, if the O_EXCL
119433d6423SLionel Sambuc flag is set this is an error */
120433d6423SLionel Sambuc } else {
121433d6423SLionel Sambuc /* Scan path name */
122433d6423SLionel Sambuc resolve.l_vmnt_lock = VMNT_READ;
123433d6423SLionel Sambuc resolve.l_vnode_lock = VNODE_OPCL;
124433d6423SLionel Sambuc if ((vp = eat_path(&resolve, fp)) == NULL) {
125433d6423SLionel Sambuc unlock_filp(filp);
126433d6423SLionel Sambuc return(err_code);
127433d6423SLionel Sambuc }
128433d6423SLionel Sambuc
129433d6423SLionel Sambuc if (vmp != NULL) unlock_vmnt(vmp);
130433d6423SLionel Sambuc }
131433d6423SLionel Sambuc
132433d6423SLionel Sambuc /* Claim the file descriptor and filp slot and fill them in. */
133232819ddSDavid van Moolenbroek fp->fp_filp[fd] = filp;
134433d6423SLionel Sambuc filp->filp_count = 1;
135433d6423SLionel Sambuc filp->filp_vno = vp;
136433d6423SLionel Sambuc filp->filp_flags = oflags;
137433d6423SLionel Sambuc if (oflags & O_CLOEXEC)
138232819ddSDavid van Moolenbroek FD_SET(fd, &fp->fp_cloexec_set);
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc /* Only do the normal open code if we didn't just create the file. */
141433d6423SLionel Sambuc if (exist) {
14256ac45c1SDavid van Moolenbroek /* Check permissions based on the given open flags, except when we are
14356ac45c1SDavid van Moolenbroek * opening an executable for the purpose of passing a file descriptor
14456ac45c1SDavid van Moolenbroek * to its interpreter for execution, in which case we check the X bit.
14556ac45c1SDavid van Moolenbroek */
14656ac45c1SDavid van Moolenbroek if ((r = forbidden(fp, vp, for_exec ? X_BIT : bits)) == OK) {
147433d6423SLionel Sambuc /* Opening reg. files, directories, and special files differ */
148433d6423SLionel Sambuc switch (vp->v_mode & S_IFMT) {
149433d6423SLionel Sambuc case S_IFREG:
150433d6423SLionel Sambuc /* Truncate regular file if O_TRUNC. */
151433d6423SLionel Sambuc if (oflags & O_TRUNC) {
152433d6423SLionel Sambuc if ((r = forbidden(fp, vp, W_BIT)) != OK)
153433d6423SLionel Sambuc break;
154433d6423SLionel Sambuc upgrade_vnode_lock(vp);
155433d6423SLionel Sambuc truncate_vnode(vp, 0);
156433d6423SLionel Sambuc }
157433d6423SLionel Sambuc break;
158433d6423SLionel Sambuc case S_IFDIR:
159433d6423SLionel Sambuc /* Directories may be read but not written. */
160433d6423SLionel Sambuc r = (bits & W_BIT ? EISDIR : OK);
161433d6423SLionel Sambuc break;
162433d6423SLionel Sambuc case S_IFCHR:
163433d6423SLionel Sambuc /* Invoke the driver for special processing. */
164433d6423SLionel Sambuc dev = vp->v_sdev;
165433d6423SLionel Sambuc /* TTY needs to know about the O_NOCTTY flag. */
166232819ddSDavid van Moolenbroek r = cdev_open(fd, dev, bits | (oflags & O_NOCTTY));
167433d6423SLionel Sambuc vp = filp->filp_vno; /* Might be updated by
168433d6423SLionel Sambuc * cdev_open after cloning */
169433d6423SLionel Sambuc break;
170433d6423SLionel Sambuc case S_IFBLK:
171433d6423SLionel Sambuc
172433d6423SLionel Sambuc lock_bsf();
173433d6423SLionel Sambuc
174433d6423SLionel Sambuc /* Invoke the driver for special processing. */
175433d6423SLionel Sambuc dev = vp->v_sdev;
176433d6423SLionel Sambuc r = bdev_open(dev, bits);
177433d6423SLionel Sambuc if (r != OK) {
178433d6423SLionel Sambuc unlock_bsf();
179433d6423SLionel Sambuc break;
180433d6423SLionel Sambuc }
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc major_dev = major(vp->v_sdev);
183433d6423SLionel Sambuc dp = &dmap[major_dev];
184433d6423SLionel Sambuc if (dp->dmap_driver == NONE) {
185433d6423SLionel Sambuc printf("VFS: block driver disappeared!\n");
186433d6423SLionel Sambuc unlock_bsf();
187433d6423SLionel Sambuc r = ENXIO;
188433d6423SLionel Sambuc break;
189433d6423SLionel Sambuc }
190433d6423SLionel Sambuc
191433d6423SLionel Sambuc /* Check whether the device is mounted or not. If so,
192433d6423SLionel Sambuc * then that FS is responsible for this device.
193433d6423SLionel Sambuc * Otherwise we default to ROOT_FS.
194433d6423SLionel Sambuc */
195433d6423SLionel Sambuc vp->v_bfs_e = ROOT_FS_E; /* By default */
196433d6423SLionel Sambuc for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp)
197433d6423SLionel Sambuc if (vmp->m_dev == vp->v_sdev &&
198433d6423SLionel Sambuc !(vmp->m_flags & VMNT_FORCEROOTBSF)) {
199433d6423SLionel Sambuc vp->v_bfs_e = vmp->m_fs_e;
200433d6423SLionel Sambuc }
201433d6423SLionel Sambuc
202433d6423SLionel Sambuc /* Send the driver label to the file system that will
203433d6423SLionel Sambuc * handle the block I/O requests (even when its label
204433d6423SLionel Sambuc * and endpoint are known already), but only when it is
205433d6423SLionel Sambuc * the root file system. Other file systems will
206433d6423SLionel Sambuc * already have it anyway.
207433d6423SLionel Sambuc */
208433d6423SLionel Sambuc if (vp->v_bfs_e != ROOT_FS_E) {
209433d6423SLionel Sambuc unlock_bsf();
210433d6423SLionel Sambuc break;
211433d6423SLionel Sambuc }
212433d6423SLionel Sambuc
213433d6423SLionel Sambuc if (req_newdriver(vp->v_bfs_e, vp->v_sdev,
214433d6423SLionel Sambuc dp->dmap_label) != OK) {
215433d6423SLionel Sambuc printf("VFS: error sending driver label\n");
216433d6423SLionel Sambuc bdev_close(dev);
217433d6423SLionel Sambuc r = ENXIO;
218433d6423SLionel Sambuc }
219433d6423SLionel Sambuc unlock_bsf();
220433d6423SLionel Sambuc break;
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc case S_IFIFO:
223433d6423SLionel Sambuc /* Create a mapped inode on PFS which handles reads
224433d6423SLionel Sambuc and writes to this named pipe. */
225433d6423SLionel Sambuc upgrade_vnode_lock(vp);
226433d6423SLionel Sambuc r = map_vnode(vp, PFS_PROC_NR);
227433d6423SLionel Sambuc if (r == OK) {
228433d6423SLionel Sambuc if (vp->v_ref_count == 1) {
229433d6423SLionel Sambuc if (vp->v_size != 0)
230433d6423SLionel Sambuc r = truncate_vnode(vp, 0);
231433d6423SLionel Sambuc }
232433d6423SLionel Sambuc oflags |= O_APPEND; /* force append mode */
233433d6423SLionel Sambuc filp->filp_flags = oflags;
234433d6423SLionel Sambuc }
235433d6423SLionel Sambuc if (r == OK) {
236232819ddSDavid van Moolenbroek r = pipe_open(fd, vp, bits, oflags);
237433d6423SLionel Sambuc }
238433d6423SLionel Sambuc if (r != ENXIO) {
239433d6423SLionel Sambuc /* See if someone else is doing a rd or wt on
240433d6423SLionel Sambuc * the FIFO. If so, use its filp entry so the
241433d6423SLionel Sambuc * file position will be automatically shared.
242433d6423SLionel Sambuc */
243433d6423SLionel Sambuc b = (bits & R_BIT ? R_BIT : W_BIT);
244433d6423SLionel Sambuc filp->filp_count = 0; /* don't find self */
245433d6423SLionel Sambuc if ((filp2 = find_filp(vp, b)) != NULL) {
246433d6423SLionel Sambuc /* Co-reader or writer found. Use it.*/
247232819ddSDavid van Moolenbroek fp->fp_filp[fd] = filp2;
248433d6423SLionel Sambuc filp2->filp_count++;
249433d6423SLionel Sambuc filp2->filp_vno = vp;
250433d6423SLionel Sambuc filp2->filp_flags = oflags;
251433d6423SLionel Sambuc
252433d6423SLionel Sambuc /* v_count was incremented after the vnode
253433d6423SLionel Sambuc * has been found. i_count was incremented
254433d6423SLionel Sambuc * incorrectly in FS, not knowing that we
255433d6423SLionel Sambuc * were going to use an existing filp
256433d6423SLionel Sambuc * entry. Correct this error.
257433d6423SLionel Sambuc */
258433d6423SLionel Sambuc unlock_vnode(vp);
259433d6423SLionel Sambuc put_vnode(vp);
260433d6423SLionel Sambuc } else {
261433d6423SLionel Sambuc /* Nobody else found. Restore filp. */
262433d6423SLionel Sambuc filp->filp_count = 1;
263433d6423SLionel Sambuc }
264433d6423SLionel Sambuc }
265433d6423SLionel Sambuc break;
2660eb6caa0SDavid van Moolenbroek case S_IFSOCK:
2670eb6caa0SDavid van Moolenbroek r = EOPNOTSUPP;
2680eb6caa0SDavid van Moolenbroek break;
2690eb6caa0SDavid van Moolenbroek default:
2700eb6caa0SDavid van Moolenbroek printf("VFS: attempt to open file <%llu,%llu> of "
2710eb6caa0SDavid van Moolenbroek "type 0%o\n", vp->v_dev, vp->v_inode_nr,
2720eb6caa0SDavid van Moolenbroek vp->v_mode & S_IFMT);
2730eb6caa0SDavid van Moolenbroek r = EIO;
274433d6423SLionel Sambuc }
275433d6423SLionel Sambuc }
276433d6423SLionel Sambuc }
277433d6423SLionel Sambuc
278433d6423SLionel Sambuc unlock_filp(filp);
279433d6423SLionel Sambuc
280433d6423SLionel Sambuc /* If error, release inode. */
281433d6423SLionel Sambuc if (r != OK) {
282433d6423SLionel Sambuc if (r != SUSPEND) {
283232819ddSDavid van Moolenbroek fp->fp_filp[fd] = NULL;
284433d6423SLionel Sambuc filp->filp_count = 0;
285433d6423SLionel Sambuc filp->filp_vno = NULL;
286433d6423SLionel Sambuc put_vnode(vp);
287433d6423SLionel Sambuc }
288433d6423SLionel Sambuc } else {
289232819ddSDavid van Moolenbroek r = fd;
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc
292433d6423SLionel Sambuc return(r);
293433d6423SLionel Sambuc }
294433d6423SLionel Sambuc
295433d6423SLionel Sambuc
296433d6423SLionel Sambuc /*===========================================================================*
297433d6423SLionel Sambuc * new_node *
298433d6423SLionel Sambuc *===========================================================================*/
new_node(struct lookup * resolve,int oflags,mode_t bits)299433d6423SLionel Sambuc static struct vnode *new_node(struct lookup *resolve, int oflags, mode_t bits)
300433d6423SLionel Sambuc {
301433d6423SLionel Sambuc /* Try to create a new inode and return a pointer to it. If the inode already
302433d6423SLionel Sambuc exists, return a pointer to it as well, but set err_code accordingly.
303433d6423SLionel Sambuc NULL is returned if the path cannot be resolved up to the last
304433d6423SLionel Sambuc directory, or when the inode cannot be created due to permissions or
305433d6423SLionel Sambuc otherwise. */
306433d6423SLionel Sambuc struct vnode *dirp, *vp;
307433d6423SLionel Sambuc struct vmnt *dir_vmp, *vp_vmp;
308433d6423SLionel Sambuc int r;
309433d6423SLionel Sambuc struct node_details res;
310433d6423SLionel Sambuc struct lookup findnode;
311433d6423SLionel Sambuc char *path;
312433d6423SLionel Sambuc
313433d6423SLionel Sambuc path = resolve->l_path; /* For easy access */
314433d6423SLionel Sambuc
315433d6423SLionel Sambuc lookup_init(&findnode, path, resolve->l_flags, &dir_vmp, &dirp);
316433d6423SLionel Sambuc findnode.l_vmnt_lock = VMNT_WRITE;
317433d6423SLionel Sambuc findnode.l_vnode_lock = VNODE_WRITE; /* dir node */
318433d6423SLionel Sambuc
319433d6423SLionel Sambuc /* When O_CREAT and O_EXCL flags are set, the path may not be named by a
320433d6423SLionel Sambuc * symbolic link. */
321433d6423SLionel Sambuc if (oflags & O_EXCL) findnode.l_flags |= PATH_RET_SYMLINK;
322433d6423SLionel Sambuc
323433d6423SLionel Sambuc /* See if the path can be opened down to the last directory. */
324433d6423SLionel Sambuc if ((dirp = last_dir(&findnode, fp)) == NULL) return(NULL);
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc /* The final directory is accessible. Get final component of the path. */
327433d6423SLionel Sambuc lookup_init(&findnode, findnode.l_path, findnode.l_flags, &vp_vmp, &vp);
328433d6423SLionel Sambuc findnode.l_vmnt_lock = VMNT_WRITE;
329433d6423SLionel Sambuc findnode.l_vnode_lock = (oflags & O_TRUNC) ? VNODE_WRITE : VNODE_OPCL;
330433d6423SLionel Sambuc vp = advance(dirp, &findnode, fp);
331433d6423SLionel Sambuc assert(vp_vmp == NULL); /* Lookup to last dir should have yielded lock
332433d6423SLionel Sambuc * on vmp or final component does not exist.
333433d6423SLionel Sambuc * Either way, vp_vmp ought to be not set.
334433d6423SLionel Sambuc */
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc /* The combination of a symlink with absolute path followed by a danglink
337433d6423SLionel Sambuc * symlink results in a new path that needs to be re-resolved entirely. */
338433d6423SLionel Sambuc if (path[0] == '/') {
339433d6423SLionel Sambuc unlock_vnode(dirp);
340433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
341433d6423SLionel Sambuc put_vnode(dirp);
342433d6423SLionel Sambuc if (vp != NULL) {
343433d6423SLionel Sambuc unlock_vnode(vp);
344433d6423SLionel Sambuc put_vnode(vp);
345433d6423SLionel Sambuc }
346433d6423SLionel Sambuc return new_node(resolve, oflags, bits);
347433d6423SLionel Sambuc }
348433d6423SLionel Sambuc
349433d6423SLionel Sambuc if (vp == NULL && err_code == ENOENT) {
350433d6423SLionel Sambuc /* Last path component does not exist. Make a new directory entry. */
351433d6423SLionel Sambuc if ((vp = get_free_vnode()) == NULL) {
352433d6423SLionel Sambuc /* Can't create new entry: out of vnodes. */
353433d6423SLionel Sambuc unlock_vnode(dirp);
354433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
355433d6423SLionel Sambuc put_vnode(dirp);
356433d6423SLionel Sambuc return(NULL);
357433d6423SLionel Sambuc }
358433d6423SLionel Sambuc
359433d6423SLionel Sambuc lock_vnode(vp, VNODE_OPCL);
360433d6423SLionel Sambuc upgrade_vmnt_lock(dir_vmp); /* Creating file, need exclusive access */
361433d6423SLionel Sambuc
362433d6423SLionel Sambuc if ((r = forbidden(fp, dirp, W_BIT|X_BIT)) != OK ||
363433d6423SLionel Sambuc (r = req_create(dirp->v_fs_e, dirp->v_inode_nr,bits, fp->fp_effuid,
364433d6423SLionel Sambuc fp->fp_effgid, path, &res)) != OK ) {
365433d6423SLionel Sambuc /* Can't create inode either due to permissions or some other
366433d6423SLionel Sambuc * problem. In case r is EEXIST, we might be dealing with a
367433d6423SLionel Sambuc * dangling symlink.*/
368433d6423SLionel Sambuc
369433d6423SLionel Sambuc /* Downgrade lock to prevent deadlock during symlink resolving*/
370433d6423SLionel Sambuc downgrade_vmnt_lock(dir_vmp);
371433d6423SLionel Sambuc
372433d6423SLionel Sambuc if (r == EEXIST) {
373433d6423SLionel Sambuc struct vnode *slp, *old_wd;
374433d6423SLionel Sambuc
375433d6423SLionel Sambuc
376433d6423SLionel Sambuc /* Resolve path up to symlink */
377433d6423SLionel Sambuc findnode.l_flags = PATH_RET_SYMLINK;
378433d6423SLionel Sambuc findnode.l_vnode_lock = VNODE_READ;
379433d6423SLionel Sambuc findnode.l_vnode = &slp;
380433d6423SLionel Sambuc slp = advance(dirp, &findnode, fp);
381433d6423SLionel Sambuc if (slp != NULL) {
382433d6423SLionel Sambuc if (S_ISLNK(slp->v_mode)) {
383433d6423SLionel Sambuc /* Get contents of link */
384433d6423SLionel Sambuc
385433d6423SLionel Sambuc r = req_rdlink(slp->v_fs_e,
386433d6423SLionel Sambuc slp->v_inode_nr,
387433d6423SLionel Sambuc VFS_PROC_NR,
388433d6423SLionel Sambuc (vir_bytes) path,
389433d6423SLionel Sambuc PATH_MAX - 1, 0);
390433d6423SLionel Sambuc if (r < 0) {
391433d6423SLionel Sambuc /* Failed to read link */
392433d6423SLionel Sambuc unlock_vnode(slp);
393433d6423SLionel Sambuc unlock_vnode(dirp);
394433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
395433d6423SLionel Sambuc put_vnode(slp);
396433d6423SLionel Sambuc put_vnode(dirp);
397433d6423SLionel Sambuc err_code = r;
398433d6423SLionel Sambuc return(NULL);
399433d6423SLionel Sambuc }
400433d6423SLionel Sambuc path[r] = '\0'; /* Terminate path */
401433d6423SLionel Sambuc }
402433d6423SLionel Sambuc unlock_vnode(slp);
403433d6423SLionel Sambuc put_vnode(slp);
404433d6423SLionel Sambuc }
405433d6423SLionel Sambuc
406433d6423SLionel Sambuc /* Try to create the inode the dangling symlink was
407433d6423SLionel Sambuc * pointing to. We have to use dirp as starting point
408433d6423SLionel Sambuc * as there might be multiple successive symlinks
409433d6423SLionel Sambuc * crossing multiple mountpoints.
410433d6423SLionel Sambuc * Unlock vnodes and vmnts as we're going to recurse.
411433d6423SLionel Sambuc */
412433d6423SLionel Sambuc unlock_vnode(dirp);
413433d6423SLionel Sambuc unlock_vnode(vp);
414433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
415433d6423SLionel Sambuc
416433d6423SLionel Sambuc old_wd = fp->fp_wd; /* Save orig. working dirp */
417433d6423SLionel Sambuc fp->fp_wd = dirp;
418433d6423SLionel Sambuc vp = new_node(resolve, oflags, bits);
419433d6423SLionel Sambuc fp->fp_wd = old_wd; /* Restore */
420433d6423SLionel Sambuc
421433d6423SLionel Sambuc if (vp != NULL) {
422433d6423SLionel Sambuc put_vnode(dirp);
423433d6423SLionel Sambuc *(resolve->l_vnode) = vp;
424433d6423SLionel Sambuc return(vp);
425433d6423SLionel Sambuc }
426433d6423SLionel Sambuc r = err_code;
427433d6423SLionel Sambuc }
428433d6423SLionel Sambuc
429433d6423SLionel Sambuc if (r == EEXIST)
430433d6423SLionel Sambuc err_code = EIO; /* Impossible, we have verified that
431433d6423SLionel Sambuc * the last component doesn't exist and
432433d6423SLionel Sambuc * is not a dangling symlink. */
433433d6423SLionel Sambuc else
434433d6423SLionel Sambuc err_code = r;
435433d6423SLionel Sambuc
436433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
437433d6423SLionel Sambuc unlock_vnode(dirp);
438433d6423SLionel Sambuc unlock_vnode(vp);
439433d6423SLionel Sambuc put_vnode(dirp);
440433d6423SLionel Sambuc return(NULL);
441433d6423SLionel Sambuc }
442433d6423SLionel Sambuc
443433d6423SLionel Sambuc /* Store results and mark vnode in use */
444433d6423SLionel Sambuc
445433d6423SLionel Sambuc vp->v_fs_e = res.fs_e;
446433d6423SLionel Sambuc vp->v_inode_nr = res.inode_nr;
447433d6423SLionel Sambuc vp->v_mode = res.fmode;
448433d6423SLionel Sambuc vp->v_size = res.fsize;
449433d6423SLionel Sambuc vp->v_uid = res.uid;
450433d6423SLionel Sambuc vp->v_gid = res.gid;
451433d6423SLionel Sambuc vp->v_sdev = res.dev;
452433d6423SLionel Sambuc vp->v_vmnt = dirp->v_vmnt;
453433d6423SLionel Sambuc vp->v_dev = vp->v_vmnt->m_dev;
454433d6423SLionel Sambuc vp->v_fs_count = 1;
455433d6423SLionel Sambuc vp->v_ref_count = 1;
456433d6423SLionel Sambuc } else {
457433d6423SLionel Sambuc /* Either last component exists, or there is some other problem. */
458433d6423SLionel Sambuc if (vp != NULL) {
459433d6423SLionel Sambuc r = EEXIST; /* File exists or a symlink names a file while
460433d6423SLionel Sambuc * O_EXCL is set. */
461433d6423SLionel Sambuc } else
462433d6423SLionel Sambuc r = err_code; /* Other problem. */
463433d6423SLionel Sambuc }
464433d6423SLionel Sambuc
465433d6423SLionel Sambuc err_code = r;
466433d6423SLionel Sambuc /* When dirp equals vp, we shouldn't release the lock as a vp is locked only
467433d6423SLionel Sambuc * once. Releasing the lock would cause the resulting vp not be locked and
468433d6423SLionel Sambuc * cause mayhem later on. */
469433d6423SLionel Sambuc if (dirp != vp) {
470433d6423SLionel Sambuc unlock_vnode(dirp);
471433d6423SLionel Sambuc }
472433d6423SLionel Sambuc unlock_vmnt(dir_vmp);
473433d6423SLionel Sambuc put_vnode(dirp);
474433d6423SLionel Sambuc
475433d6423SLionel Sambuc *(resolve->l_vnode) = vp;
476433d6423SLionel Sambuc return(vp);
477433d6423SLionel Sambuc }
478433d6423SLionel Sambuc
479433d6423SLionel Sambuc
480433d6423SLionel Sambuc /*===========================================================================*
481433d6423SLionel Sambuc * pipe_open *
482433d6423SLionel Sambuc *===========================================================================*/
pipe_open(int fd,struct vnode * vp,mode_t bits,int oflags)483232819ddSDavid van Moolenbroek static int pipe_open(int fd, struct vnode *vp, mode_t bits, int oflags)
484433d6423SLionel Sambuc {
485433d6423SLionel Sambuc /* This function is called from common_open. It checks if
486433d6423SLionel Sambuc * there is at least one reader/writer pair for the pipe, if not
487433d6423SLionel Sambuc * it suspends the caller, otherwise it revives all other blocked
488433d6423SLionel Sambuc * processes hanging on the pipe.
489433d6423SLionel Sambuc */
490433d6423SLionel Sambuc
491433d6423SLionel Sambuc if ((bits & (R_BIT|W_BIT)) == (R_BIT|W_BIT)) return(ENXIO);
492433d6423SLionel Sambuc
493433d6423SLionel Sambuc /* Find the reader/writer at the other end of the pipe */
494433d6423SLionel Sambuc if (find_filp(vp, bits & W_BIT ? R_BIT : W_BIT) == NULL) {
495433d6423SLionel Sambuc /* Not found */
496433d6423SLionel Sambuc if (oflags & O_NONBLOCK) {
497433d6423SLionel Sambuc if (bits & W_BIT) return(ENXIO);
498433d6423SLionel Sambuc } else {
499433d6423SLionel Sambuc /* Let's wait for the other side to show up */
500232819ddSDavid van Moolenbroek fp->fp_popen.fd = fd;
501433d6423SLionel Sambuc suspend(FP_BLOCKED_ON_POPEN);
502433d6423SLionel Sambuc return(SUSPEND);
503433d6423SLionel Sambuc }
504433d6423SLionel Sambuc } else if (susp_count > 0) { /* revive blocked processes */
505433d6423SLionel Sambuc release(vp, VFS_OPEN, susp_count);
506433d6423SLionel Sambuc }
507433d6423SLionel Sambuc return(OK);
508433d6423SLionel Sambuc }
509433d6423SLionel Sambuc
510433d6423SLionel Sambuc
511433d6423SLionel Sambuc /*===========================================================================*
512433d6423SLionel Sambuc * do_mknod *
513433d6423SLionel Sambuc *===========================================================================*/
do_mknod(void)514433d6423SLionel Sambuc int do_mknod(void)
515433d6423SLionel Sambuc {
516433d6423SLionel Sambuc /* Perform the mknod(name, mode, addr) system call. */
517433d6423SLionel Sambuc register mode_t bits, mode_bits;
518433d6423SLionel Sambuc int r;
519433d6423SLionel Sambuc struct vnode *vp;
520433d6423SLionel Sambuc struct vmnt *vmp;
521433d6423SLionel Sambuc char fullpath[PATH_MAX];
522433d6423SLionel Sambuc struct lookup resolve;
523433d6423SLionel Sambuc vir_bytes vname1;
524433d6423SLionel Sambuc size_t vname1_length;
525433d6423SLionel Sambuc dev_t dev;
526433d6423SLionel Sambuc
527433d6423SLionel Sambuc vname1 = job_m_in.m_lc_vfs_mknod.name;
528433d6423SLionel Sambuc vname1_length = job_m_in.m_lc_vfs_mknod.len;
529433d6423SLionel Sambuc mode_bits = job_m_in.m_lc_vfs_mknod.mode;
530433d6423SLionel Sambuc dev = job_m_in.m_lc_vfs_mknod.device;
531433d6423SLionel Sambuc
532a758ec67SDavid van Moolenbroek /* If the path names a symbolic link, mknod() shall fail with EEXIST. */
533a758ec67SDavid van Moolenbroek lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
534433d6423SLionel Sambuc resolve.l_vmnt_lock = VMNT_WRITE;
535433d6423SLionel Sambuc resolve.l_vnode_lock = VNODE_WRITE;
536433d6423SLionel Sambuc
537433d6423SLionel Sambuc /* Only the super_user may make nodes other than fifos. */
538*27852ebeSDavid van Moolenbroek if (!super_user && !S_ISFIFO(mode_bits))
539433d6423SLionel Sambuc return(EPERM);
540*27852ebeSDavid van Moolenbroek
541433d6423SLionel Sambuc bits = (mode_bits & S_IFMT) | (mode_bits & ACCESSPERMS & fp->fp_umask);
542433d6423SLionel Sambuc
543433d6423SLionel Sambuc /* Open directory that's going to hold the new node. */
544433d6423SLionel Sambuc if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
545433d6423SLionel Sambuc if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
546433d6423SLionel Sambuc
547433d6423SLionel Sambuc /* Make sure that the object is a directory */
548433d6423SLionel Sambuc if (!S_ISDIR(vp->v_mode)) {
549433d6423SLionel Sambuc r = ENOTDIR;
550433d6423SLionel Sambuc } else if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
551433d6423SLionel Sambuc r = req_mknod(vp->v_fs_e, vp->v_inode_nr, fullpath, fp->fp_effuid,
552433d6423SLionel Sambuc fp->fp_effgid, bits, dev);
553433d6423SLionel Sambuc }
554433d6423SLionel Sambuc
555433d6423SLionel Sambuc unlock_vnode(vp);
556433d6423SLionel Sambuc unlock_vmnt(vmp);
557433d6423SLionel Sambuc put_vnode(vp);
558433d6423SLionel Sambuc return(r);
559433d6423SLionel Sambuc }
560433d6423SLionel Sambuc
561433d6423SLionel Sambuc /*===========================================================================*
562433d6423SLionel Sambuc * do_mkdir *
563433d6423SLionel Sambuc *===========================================================================*/
do_mkdir(void)564433d6423SLionel Sambuc int do_mkdir(void)
565433d6423SLionel Sambuc {
566433d6423SLionel Sambuc /* Perform the mkdir(name, mode) system call. */
567433d6423SLionel Sambuc mode_t bits; /* mode bits for the new inode */
568433d6423SLionel Sambuc int r;
569433d6423SLionel Sambuc struct vnode *vp;
570433d6423SLionel Sambuc struct vmnt *vmp;
571433d6423SLionel Sambuc char fullpath[PATH_MAX];
572433d6423SLionel Sambuc struct lookup resolve;
573433d6423SLionel Sambuc mode_t dirmode;
574433d6423SLionel Sambuc
575433d6423SLionel Sambuc if (copy_path(fullpath, sizeof(fullpath)) != OK)
576433d6423SLionel Sambuc return(err_code);
577433d6423SLionel Sambuc dirmode = job_m_in.m_lc_vfs_path.mode;
578433d6423SLionel Sambuc
579433d6423SLionel Sambuc lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
580433d6423SLionel Sambuc resolve.l_vmnt_lock = VMNT_WRITE;
581433d6423SLionel Sambuc resolve.l_vnode_lock = VNODE_WRITE;
582433d6423SLionel Sambuc
583433d6423SLionel Sambuc bits = I_DIRECTORY | (dirmode & RWX_MODES & fp->fp_umask);
584433d6423SLionel Sambuc if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
585433d6423SLionel Sambuc
586433d6423SLionel Sambuc /* Make sure that the object is a directory */
587433d6423SLionel Sambuc if (!S_ISDIR(vp->v_mode)) {
588433d6423SLionel Sambuc r = ENOTDIR;
589433d6423SLionel Sambuc } else if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
590433d6423SLionel Sambuc r = req_mkdir(vp->v_fs_e, vp->v_inode_nr, fullpath, fp->fp_effuid,
591433d6423SLionel Sambuc fp->fp_effgid, bits);
592433d6423SLionel Sambuc }
593433d6423SLionel Sambuc
594433d6423SLionel Sambuc unlock_vnode(vp);
595433d6423SLionel Sambuc unlock_vmnt(vmp);
596433d6423SLionel Sambuc put_vnode(vp);
597433d6423SLionel Sambuc return(r);
598433d6423SLionel Sambuc }
599433d6423SLionel Sambuc
600433d6423SLionel Sambuc /*===========================================================================*
601433d6423SLionel Sambuc * actual_lseek *
602433d6423SLionel Sambuc *===========================================================================*/
actual_lseek(struct fproc * rfp,int seekfd,int seekwhence,off_t offset,off_t * newposp)603433d6423SLionel Sambuc int actual_lseek(struct fproc *rfp, int seekfd, int seekwhence, off_t offset,
604433d6423SLionel Sambuc off_t *newposp)
605433d6423SLionel Sambuc {
606433d6423SLionel Sambuc register struct filp *rfilp;
607433d6423SLionel Sambuc int r = OK;
608433d6423SLionel Sambuc off_t pos, newpos;
609433d6423SLionel Sambuc
610433d6423SLionel Sambuc /* Check to see if the file descriptor is valid. */
611433d6423SLionel Sambuc if ( (rfilp = get_filp2(rfp, seekfd, VNODE_READ)) == NULL) {
612433d6423SLionel Sambuc return(err_code);
613433d6423SLionel Sambuc }
614433d6423SLionel Sambuc
615433d6423SLionel Sambuc /* No lseek on pipes. */
616433d6423SLionel Sambuc if (S_ISFIFO(rfilp->filp_vno->v_mode)) {
617433d6423SLionel Sambuc unlock_filp(rfilp);
618433d6423SLionel Sambuc return(ESPIPE);
619433d6423SLionel Sambuc }
620433d6423SLionel Sambuc
621433d6423SLionel Sambuc /* The value of 'whence' determines the start position to use. */
622433d6423SLionel Sambuc switch(seekwhence) {
623433d6423SLionel Sambuc case SEEK_SET: pos = 0; break;
624433d6423SLionel Sambuc case SEEK_CUR: pos = rfilp->filp_pos; break;
625433d6423SLionel Sambuc case SEEK_END: pos = rfilp->filp_vno->v_size; break;
626433d6423SLionel Sambuc default: unlock_filp(rfilp); return(EINVAL);
627433d6423SLionel Sambuc }
628433d6423SLionel Sambuc
629433d6423SLionel Sambuc newpos = pos + offset;
630433d6423SLionel Sambuc
631433d6423SLionel Sambuc /* Check for overflow. */
632433d6423SLionel Sambuc if ((offset > 0) && (newpos <= pos)) {
633433d6423SLionel Sambuc r = EOVERFLOW;
634433d6423SLionel Sambuc } else if ((offset < 0) && (newpos >= pos)) {
635433d6423SLionel Sambuc r = EOVERFLOW;
636433d6423SLionel Sambuc } else {
637433d6423SLionel Sambuc if (newposp != NULL) *newposp = newpos;
638433d6423SLionel Sambuc
639433d6423SLionel Sambuc if (newpos != rfilp->filp_pos) {
640433d6423SLionel Sambuc rfilp->filp_pos = newpos;
641433d6423SLionel Sambuc
642433d6423SLionel Sambuc /* Inhibit read ahead request */
643433d6423SLionel Sambuc r = req_inhibread(rfilp->filp_vno->v_fs_e,
644433d6423SLionel Sambuc rfilp->filp_vno->v_inode_nr);
645433d6423SLionel Sambuc }
646433d6423SLionel Sambuc }
647433d6423SLionel Sambuc
648433d6423SLionel Sambuc unlock_filp(rfilp);
649433d6423SLionel Sambuc return(r);
650433d6423SLionel Sambuc }
651433d6423SLionel Sambuc
652433d6423SLionel Sambuc /*===========================================================================*
653433d6423SLionel Sambuc * do_lseek *
654433d6423SLionel Sambuc *===========================================================================*/
do_lseek(void)655433d6423SLionel Sambuc int do_lseek(void)
656433d6423SLionel Sambuc {
657433d6423SLionel Sambuc /* Perform the lseek(2) system call. */
658e1cdaee1SLionel Sambuc off_t newpos = 0;
659433d6423SLionel Sambuc int r;
660433d6423SLionel Sambuc
661433d6423SLionel Sambuc if ((r = actual_lseek(fp, job_m_in.m_lc_vfs_lseek.fd,
662433d6423SLionel Sambuc job_m_in.m_lc_vfs_lseek.whence, job_m_in.m_lc_vfs_lseek.offset,
663433d6423SLionel Sambuc &newpos)) != OK)
664433d6423SLionel Sambuc return r;
665433d6423SLionel Sambuc
666433d6423SLionel Sambuc /* insert the new position into the output message */
667433d6423SLionel Sambuc job_m_out.m_vfs_lc_lseek.offset = newpos;
668433d6423SLionel Sambuc return OK;
669433d6423SLionel Sambuc }
670433d6423SLionel Sambuc
671433d6423SLionel Sambuc /*===========================================================================*
672433d6423SLionel Sambuc * do_close *
673433d6423SLionel Sambuc *===========================================================================*/
do_close(void)674433d6423SLionel Sambuc int do_close(void)
675433d6423SLionel Sambuc {
676491d647aSDavid van Moolenbroek /* Perform the close(fd) or closenb(fd) system call. */
677491d647aSDavid van Moolenbroek int fd, nblock;
678491d647aSDavid van Moolenbroek
679491d647aSDavid van Moolenbroek fd = job_m_in.m_lc_vfs_close.fd;
680491d647aSDavid van Moolenbroek nblock = job_m_in.m_lc_vfs_close.nblock;
681491d647aSDavid van Moolenbroek
682491d647aSDavid van Moolenbroek return close_fd(fp, fd, !nblock /*may_suspend*/);
683433d6423SLionel Sambuc }
684433d6423SLionel Sambuc
685433d6423SLionel Sambuc
686433d6423SLionel Sambuc /*===========================================================================*
687433d6423SLionel Sambuc * close_fd *
688433d6423SLionel Sambuc *===========================================================================*/
689a0814afbSRichard Sailer int
close_fd(struct fproc * rfp,int fd_nr,int may_suspend)690491d647aSDavid van Moolenbroek close_fd(struct fproc * rfp, int fd_nr, int may_suspend)
691433d6423SLionel Sambuc {
692433d6423SLionel Sambuc /* Perform the close(fd) system call. */
693433d6423SLionel Sambuc register struct filp *rfilp;
694433d6423SLionel Sambuc register struct vnode *vp;
695433d6423SLionel Sambuc struct file_lock *flp;
696491d647aSDavid van Moolenbroek int r, lock_count;
697433d6423SLionel Sambuc
698433d6423SLionel Sambuc /* First locate the vnode that belongs to the file descriptor. */
699433d6423SLionel Sambuc if ( (rfilp = get_filp2(rfp, fd_nr, VNODE_OPCL)) == NULL) return(err_code);
700433d6423SLionel Sambuc
701433d6423SLionel Sambuc vp = rfilp->filp_vno;
702433d6423SLionel Sambuc
703433d6423SLionel Sambuc /* first, make all future get_filp2()'s fail; otherwise
704433d6423SLionel Sambuc * we might try to close the same fd in different threads
705433d6423SLionel Sambuc */
706433d6423SLionel Sambuc rfp->fp_filp[fd_nr] = NULL;
707433d6423SLionel Sambuc
708491d647aSDavid van Moolenbroek r = close_filp(rfilp, may_suspend);
709433d6423SLionel Sambuc
710433d6423SLionel Sambuc FD_CLR(fd_nr, &rfp->fp_cloexec_set);
711433d6423SLionel Sambuc
712433d6423SLionel Sambuc /* Check to see if the file is locked. If so, release all locks. */
713433d6423SLionel Sambuc if (nr_locks > 0) {
714433d6423SLionel Sambuc lock_count = nr_locks; /* save count of locks */
715433d6423SLionel Sambuc for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) {
716433d6423SLionel Sambuc if (flp->lock_type == 0) continue; /* slot not in use */
717433d6423SLionel Sambuc if (flp->lock_vnode == vp && flp->lock_pid == rfp->fp_pid) {
718433d6423SLionel Sambuc flp->lock_type = 0;
719433d6423SLionel Sambuc nr_locks--;
720433d6423SLionel Sambuc }
721433d6423SLionel Sambuc }
722433d6423SLionel Sambuc if (nr_locks < lock_count)
723433d6423SLionel Sambuc lock_revive(); /* one or more locks released */
724433d6423SLionel Sambuc }
725433d6423SLionel Sambuc
726491d647aSDavid van Moolenbroek return(r);
727433d6423SLionel Sambuc }
728