opendir change: refinement
[minix.git] / servers / vfs / link.c
blob0b68f4d2a2550620f428a1e3480b4570de387feb
1 /* This file handles the LINK and UNLINK system calls. It also deals with
2 * deallocating the storage used by a file when the last UNLINK is done to a
3 * file and the blocks must be returned to the free block pool.
5 * The entry points into this file are
6 * do_link: perform the LINK system call
7 * do_unlink: perform the UNLINK and RMDIR system calls
8 * do_rename: perform the RENAME system call
9 * do_truncate: perform the TRUNCATE system call
10 * do_ftruncate: perform the FTRUNCATE system call
11 * do_rdlink: perform the RDLNK system call
14 #include "fs.h"
15 #include <sys/stat.h>
16 #include <string.h>
17 #include <minix/com.h>
18 #include <minix/callnr.h>
19 #include <minix/vfsif.h>
20 #include <dirent.h>
21 #include <assert.h>
22 #include "file.h"
23 #include "fproc.h"
24 #include "path.h"
25 #include "vnode.h"
26 #include "param.h"
27 #include "scratchpad.h"
29 /*===========================================================================*
30 * do_link *
31 *===========================================================================*/
32 int do_link()
34 /* Perform the link(name1, name2) system call. */
35 int r = OK;
36 struct vnode *vp = NULL, *dirp = NULL;
37 struct vmnt *vmp1 = NULL, *vmp2 = NULL;
38 char fullpath[PATH_MAX];
39 struct lookup resolve;
40 vir_bytes vname1, vname2;
41 size_t vname1_length, vname2_length;
43 vname1 = (vir_bytes) job_m_in.name1;
44 vname1_length = job_m_in.name1_length;
45 vname2 = (vir_bytes) job_m_in.name2;
46 vname2_length = job_m_in.name2_length;
48 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp);
49 resolve.l_vmnt_lock = VMNT_WRITE;
50 resolve.l_vnode_lock = VNODE_READ;
52 /* See if 'name1' (file to be linked to) exists. */
53 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
54 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
56 /* Does the final directory of 'name2' exist? */
57 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp2, &dirp);
58 resolve.l_vmnt_lock = VMNT_READ;
59 resolve.l_vnode_lock = VNODE_WRITE;
60 if (fetch_name(vname2, vname2_length, fullpath) != OK)
61 r = err_code;
62 else if ((dirp = last_dir(&resolve, fp)) == NULL)
63 r = err_code;
65 if (r != OK) {
66 unlock_vnode(vp);
67 unlock_vmnt(vmp1);
68 put_vnode(vp);
69 return(r);
72 /* Check for links across devices. */
73 if (vp->v_fs_e != dirp->v_fs_e)
74 r = EXDEV;
75 else
76 r = forbidden(fp, dirp, W_BIT | X_BIT);
78 if (r == OK)
79 r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath,
80 vp->v_inode_nr);
82 unlock_vnode(vp);
83 unlock_vnode(dirp);
84 if (vmp2 != NULL) unlock_vmnt(vmp2);
85 unlock_vmnt(vmp1);
86 put_vnode(vp);
87 put_vnode(dirp);
88 return(r);
91 /*===========================================================================*
92 * do_unlink *
93 *===========================================================================*/
94 int do_unlink()
96 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
97 * is almost the same. They differ only in some condition testing. Unlink()
98 * may be used by the superuser to do dangerous things; rmdir() may not.
99 * The syscall might provide 'name' embedded in the message.
101 struct vnode *dirp, *dirp_l, *vp;
102 struct vmnt *vmp, *vmp2;
103 int r;
104 char fullpath[PATH_MAX];
105 struct lookup resolve, stickycheck;
106 vir_bytes vname;
107 size_t vname_length;
109 vname = (vir_bytes) job_m_in.name;
110 vname_length = job_m_in.name_length;
111 if (copy_name(vname_length, fullpath) != OK) {
112 /* Direct copy failed, try fetching from user space */
113 if (fetch_name(vname, vname_length, fullpath) != OK)
114 return(err_code);
117 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &dirp_l);
118 resolve.l_vmnt_lock = VMNT_WRITE;
119 resolve.l_vnode_lock = VNODE_WRITE;
121 /* Get the last directory in the path. */
122 if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
124 /* Make sure that the object is a directory */
125 if (!S_ISDIR(dirp->v_mode)) {
126 unlock_vnode(dirp);
127 unlock_vmnt(vmp);
128 put_vnode(dirp);
129 return(ENOTDIR);
132 /* The caller must have both search and execute permission */
133 if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
134 unlock_vnode(dirp);
135 unlock_vmnt(vmp);
136 put_vnode(dirp);
137 return(r);
140 /* Also, if the sticky bit is set, only the owner of the file or a privileged
141 user is allowed to unlink */
142 if ((dirp->v_mode & S_ISVTX) == S_ISVTX) {
143 /* Look up inode of file to unlink to retrieve owner */
144 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
145 stickycheck.l_vmnt_lock = VMNT_READ;
146 stickycheck.l_vnode_lock = VNODE_READ;
147 vp = advance(dirp, &stickycheck, fp);
148 assert(vmp2 == NULL);
149 if (vp != NULL) {
150 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
151 r = EPERM;
152 unlock_vnode(vp);
153 put_vnode(vp);
154 } else
155 r = err_code;
156 if (r != OK) {
157 unlock_vnode(dirp);
158 unlock_vmnt(vmp);
159 put_vnode(dirp);
160 return(r);
164 upgrade_vmnt_lock(vmp);
166 if (job_call_nr == UNLINK)
167 r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
168 else
169 r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
170 unlock_vnode(dirp);
171 unlock_vmnt(vmp);
172 put_vnode(dirp);
173 return(r);
176 /*===========================================================================*
177 * do_rename *
178 *===========================================================================*/
179 int do_rename()
181 /* Perform the rename(name1, name2) system call. */
182 int r = OK, r1;
183 struct vnode *old_dirp = NULL, *new_dirp = NULL, *new_dirp_l = NULL, *vp;
184 struct vmnt *oldvmp, *newvmp, *vmp2;
185 char old_name[PATH_MAX];
186 char fullpath[PATH_MAX];
187 struct lookup resolve, stickycheck;
188 vir_bytes vname1, vname2;
189 size_t vname1_length, vname2_length;
191 vname1 = (vir_bytes) job_m_in.name1;
192 vname1_length = job_m_in.name1_length;
193 vname2 = (vir_bytes) job_m_in.name2;
194 vname2_length = job_m_in.name2_length;
196 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &oldvmp, &old_dirp);
197 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
198 resolve.l_vmnt_lock = VMNT_WRITE;
199 resolve.l_vnode_lock = VNODE_WRITE;
201 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
202 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
203 if ((old_dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
205 /* If the sticky bit is set, only the owner of the file or a privileged
206 user is allowed to rename */
207 if ((old_dirp->v_mode & S_ISVTX) == S_ISVTX) {
208 /* Look up inode of file to unlink to retrieve owner */
209 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
210 stickycheck.l_vmnt_lock = VMNT_READ;
211 stickycheck.l_vnode_lock = VNODE_READ;
212 vp = advance(old_dirp, &stickycheck, fp);
213 assert(vmp2 == NULL);
214 if (vp != NULL) {
215 if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
216 r = EPERM;
217 unlock_vnode(vp);
218 put_vnode(vp);
219 } else
220 r = err_code;
221 if (r != OK) {
222 unlock_vnode(old_dirp);
223 unlock_vmnt(oldvmp);
224 put_vnode(old_dirp);
225 return(r);
229 /* Save the last component of the old name */
230 if (strlen(fullpath) >= sizeof(old_name)) {
231 unlock_vnode(old_dirp);
232 unlock_vmnt(oldvmp);
233 put_vnode(old_dirp);
234 return(ENAMETOOLONG);
236 strlcpy(old_name, fullpath, PATH_MAX);
238 /* See if 'name2' (new name) exists. Get dir inode */
239 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &newvmp, &new_dirp_l);
240 resolve.l_vmnt_lock = VMNT_READ;
241 resolve.l_vnode_lock = VNODE_WRITE;
242 if (fetch_name(vname2, vname2_length, fullpath) != OK) r = err_code;
243 else if ((new_dirp = last_dir(&resolve, fp)) == NULL) r = err_code;
245 /* We used a separate vnode pointer to see whether we obtained a lock on the
246 * new_dirp vnode. If the new directory and old directory are the same, then
247 * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will
248 * be NULL, but new_dirp will not.
250 if (new_dirp == old_dirp) assert(new_dirp_l == NULL);
252 if (r != OK) {
253 unlock_vnode(old_dirp);
254 unlock_vmnt(oldvmp);
255 put_vnode(old_dirp);
256 return(r);
259 /* Both parent directories must be on the same device. */
260 if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
262 /* Parent dirs must be writable, searchable and on a writable device */
263 if ((r1 = forbidden(fp, old_dirp, W_BIT|X_BIT)) != OK ||
264 (r1 = forbidden(fp, new_dirp, W_BIT|X_BIT)) != OK) r = r1;
266 if (r == OK) {
267 upgrade_vmnt_lock(oldvmp); /* Upgrade to exclusive access */
268 r = req_rename(old_dirp->v_fs_e, old_dirp->v_inode_nr, old_name,
269 new_dirp->v_inode_nr, fullpath);
272 unlock_vnode(old_dirp);
273 unlock_vmnt(oldvmp);
274 if (new_dirp_l) unlock_vnode(new_dirp_l);
275 if (newvmp) unlock_vmnt(newvmp);
277 put_vnode(old_dirp);
278 put_vnode(new_dirp);
280 return(r);
283 /*===========================================================================*
284 * do_truncate *
285 *===========================================================================*/
286 int do_truncate()
288 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
289 * do_truncate() and do_ftruncate() have to get hold of the inode, either
290 * by name or fd, do checks on it, and call truncate_inode() to do the
291 * work.
293 struct vnode *vp;
294 struct vmnt *vmp;
295 int r;
296 char fullpath[PATH_MAX];
297 struct lookup resolve;
298 off_t length;
299 vir_bytes vname;
300 size_t vname_length;
302 vname = (vir_bytes) job_m_in.m2_p1;
303 vname_length = job_m_in.m2_i1;
305 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
306 resolve.l_vmnt_lock = VMNT_READ;
307 resolve.l_vnode_lock = VNODE_WRITE;
309 length = (off_t) job_m_in.flength;
310 if (length < 0) return(EINVAL);
312 /* Temporarily open file */
313 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
314 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
316 /* Ask FS to truncate the file */
317 if ((r = forbidden(fp, vp, W_BIT)) == OK) {
318 /* If the file size does not change, do not make the actual call. This
319 * ensures that the file times are retained when the file size remains
320 * the same, which is a POSIX requirement.
322 if (S_ISREG(vp->v_mode) && vp->v_size == length)
323 r = OK;
324 else
325 r = truncate_vnode(vp, length);
328 unlock_vnode(vp);
329 unlock_vmnt(vmp);
330 put_vnode(vp);
331 return(r);
334 /*===========================================================================*
335 * do_ftruncate *
336 *===========================================================================*/
337 int do_ftruncate()
339 /* As with do_truncate(), truncate_vnode() does the actual work. */
340 struct filp *rfilp;
341 struct vnode *vp;
342 int r;
343 off_t length;
345 scratch(fp).file.fd_nr = job_m_in.fd;
346 length = (off_t) job_m_in.flength;
348 if (length < 0) return(EINVAL);
350 /* File is already opened; get a vnode pointer from filp */
351 if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
352 return(err_code);
354 vp = rfilp->filp_vno;
356 if (!(rfilp->filp_mode & W_BIT))
357 r = EBADF;
358 else if (S_ISREG(vp->v_mode) && vp->v_size == length)
359 /* If the file size does not change, do not make the actual call. This
360 * ensures that the file times are retained when the file size remains
361 * the same, which is a POSIX requirement.
363 r = OK;
364 else
365 r = truncate_vnode(vp, length);
367 unlock_filp(rfilp);
368 return(r);
372 /*===========================================================================*
373 * truncate_vnode *
374 *===========================================================================*/
375 int truncate_vnode(vp, newsize)
376 struct vnode *vp;
377 off_t newsize;
379 /* Truncate a regular file or a pipe */
380 int r;
382 assert(tll_locked_by_me(&vp->v_lock));
383 if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
385 /* We must not compare the old and the new size here: this function may be
386 * called for open(2), which requires an update to the file times if O_TRUNC
387 * is given, even if the file size remains the same.
389 if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
390 vp->v_size = newsize;
391 return(r);
395 /*===========================================================================*
396 * do_slink *
397 *===========================================================================*/
398 int do_slink()
400 /* Perform the symlink(name1, name2) system call. */
401 int r;
402 struct vnode *vp;
403 struct vmnt *vmp;
404 char fullpath[PATH_MAX];
405 struct lookup resolve;
406 vir_bytes vname1, vname2;
407 size_t vname1_length, vname2_length;
409 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
410 resolve.l_vmnt_lock = VMNT_WRITE;
411 resolve.l_vnode_lock = VNODE_WRITE;
413 vname1 = (vir_bytes) job_m_in.name1;
414 vname1_length = job_m_in.name1_length;
415 vname2 = (vir_bytes) job_m_in.name2;
416 vname2_length = job_m_in.name2_length;
418 if (vname1_length <= 1) return(ENOENT);
419 if (vname1_length >= SYMLINK_MAX) return(ENAMETOOLONG);
421 /* Get dir inode of 'name2' */
422 if (fetch_name(vname2, vname2_length, fullpath) != OK) return(err_code);
423 if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
424 if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
425 r = req_slink(vp->v_fs_e, vp->v_inode_nr, fullpath, who_e,
426 vname1, vname1_length - 1, fp->fp_effuid,
427 fp->fp_effgid);
430 unlock_vnode(vp);
431 unlock_vmnt(vmp);
432 put_vnode(vp);
434 return(r);
437 /*===========================================================================*
438 * rdlink_direct *
439 *===========================================================================*/
440 int rdlink_direct(orig_path, link_path, rfp)
441 char *orig_path;
442 char link_path[PATH_MAX]; /* should have length PATH_MAX */
443 struct fproc *rfp;
445 /* Perform a readlink()-like call from within the VFS */
446 int r;
447 struct vnode *vp;
448 struct vmnt *vmp;
449 struct lookup resolve;
451 lookup_init(&resolve, link_path, PATH_RET_SYMLINK, &vmp, &vp);
452 resolve.l_vmnt_lock = VMNT_READ;
453 resolve.l_vnode_lock = VNODE_READ;
455 /* Temporarily open the file containing the symbolic link. Use link_path
456 * for temporary storage to keep orig_path untouched. */
457 strncpy(link_path, orig_path, PATH_MAX); /* PATH_MAX includes '\0' */
458 link_path[PATH_MAX - 1] = '\0';
459 if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
461 /* Make sure this is a symbolic link */
462 if (!S_ISLNK(vp->v_mode))
463 r = EINVAL;
464 else
465 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path,
466 PATH_MAX - 1, 1);
468 if (r > 0) link_path[r] = '\0'; /* Terminate string when succesful */
470 unlock_vnode(vp);
471 unlock_vmnt(vmp);
472 put_vnode(vp);
474 return r;
477 /*===========================================================================*
478 * do_rdlink *
479 *===========================================================================*/
480 int do_rdlink()
482 /* Perform the readlink(name, buf, bufsize) system call. */
483 int r;
484 struct vnode *vp;
485 struct vmnt *vmp;
486 char fullpath[PATH_MAX];
487 struct lookup resolve;
488 vir_bytes vname;
489 size_t vname_length, buf_size;
490 vir_bytes buf;
492 vname = (vir_bytes) job_m_in.name1;
493 vname_length = job_m_in.name1_length;
494 buf = (vir_bytes) job_m_in.name2;
495 buf_size = (size_t) job_m_in.nbytes;
496 if (buf_size > SSIZE_MAX) return(EINVAL);
498 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
499 resolve.l_vmnt_lock = VMNT_READ;
500 resolve.l_vnode_lock = VNODE_READ;
502 /* Temporarily open the file containing the symbolic link */
503 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
504 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
506 /* Make sure this is a symbolic link */
507 if (!S_ISLNK(vp->v_mode))
508 r = EINVAL;
509 else
510 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0);
512 unlock_vnode(vp);
513 unlock_vmnt(vmp);
514 put_vnode(vp);
516 return(r);