tools/llvm: Do not build with symbols
[minix3.git] / minix / servers / vfs / link.c
blobd4aed5128fc90a08a006adb19f7ad9e23001682d
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 <sys/dirent.h>
21 #include <assert.h>
22 #include "file.h"
23 #include "path.h"
24 #include "vnode.h"
25 #include "scratchpad.h"
27 /*===========================================================================*
28 * do_link *
29 *===========================================================================*/
30 int do_link(void)
32 /* Perform the link(name1, name2) system call. */
33 int r = OK;
34 struct vnode *vp = NULL, *dirp = NULL;
35 struct vmnt *vmp1 = NULL, *vmp2 = NULL;
36 char fullpath[PATH_MAX];
37 struct lookup resolve;
38 vir_bytes vname1, vname2;
39 size_t vname1_length, vname2_length;
41 vname1 = job_m_in.m_lc_vfs_link.name1;
42 vname1_length = job_m_in.m_lc_vfs_link.len1;
43 vname2 = job_m_in.m_lc_vfs_link.name2;
44 vname2_length = job_m_in.m_lc_vfs_link.len2;
46 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp);
47 resolve.l_vmnt_lock = VMNT_WRITE;
48 resolve.l_vnode_lock = VNODE_READ;
50 /* See if 'name1' (file to be linked to) exists. */
51 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
52 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
54 /* Does the final directory of 'name2' exist? */
55 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp2, &dirp);
56 resolve.l_vmnt_lock = VMNT_READ;
57 resolve.l_vnode_lock = VNODE_WRITE;
58 if (fetch_name(vname2, vname2_length, fullpath) != OK)
59 r = err_code;
60 else if ((dirp = last_dir(&resolve, fp)) == NULL)
61 r = err_code;
63 if (r != OK) {
64 unlock_vnode(vp);
65 unlock_vmnt(vmp1);
66 put_vnode(vp);
67 return(r);
70 /* Check for links across devices. */
71 if (vp->v_fs_e != dirp->v_fs_e)
72 r = EXDEV;
73 else
74 r = forbidden(fp, dirp, W_BIT | X_BIT);
76 if (r == OK)
77 r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath,
78 vp->v_inode_nr);
80 unlock_vnode(vp);
81 unlock_vnode(dirp);
82 if (vmp2 != NULL) unlock_vmnt(vmp2);
83 unlock_vmnt(vmp1);
84 put_vnode(vp);
85 put_vnode(dirp);
86 return(r);
89 /*===========================================================================*
90 * do_unlink *
91 *===========================================================================*/
92 int do_unlink(void)
94 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
95 * is almost the same. They differ only in some condition testing. Unlink()
96 * may be used by the superuser to do dangerous things; rmdir() may not.
97 * The syscall might provide 'name' embedded in the message.
99 struct vnode *dirp, *dirp_l, *vp;
100 struct vmnt *vmp, *vmp2;
101 int r;
102 char fullpath[PATH_MAX];
103 struct lookup resolve, stickycheck;
105 if (copy_path(fullpath, sizeof(fullpath)) != OK)
106 return(err_code);
108 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &dirp_l);
109 resolve.l_vmnt_lock = VMNT_WRITE;
110 resolve.l_vnode_lock = VNODE_WRITE;
112 /* Get the last directory in the path. */
113 if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
115 /* Make sure that the object is a directory */
116 if (!S_ISDIR(dirp->v_mode)) {
117 unlock_vnode(dirp);
118 unlock_vmnt(vmp);
119 put_vnode(dirp);
120 return(ENOTDIR);
123 /* The caller must have both search and execute permission */
124 if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
125 unlock_vnode(dirp);
126 unlock_vmnt(vmp);
127 put_vnode(dirp);
128 return(r);
131 /* Also, if the sticky bit is set, only the owner of the file or a privileged
132 user is allowed to unlink */
133 if ((dirp->v_mode & S_ISVTX) == S_ISVTX) {
134 /* Look up inode of file to unlink to retrieve owner */
135 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
136 stickycheck.l_vmnt_lock = VMNT_READ;
137 stickycheck.l_vnode_lock = VNODE_READ;
138 vp = advance(dirp, &stickycheck, fp);
139 assert(vmp2 == NULL);
140 if (vp != NULL) {
141 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
142 r = EPERM;
143 unlock_vnode(vp);
144 put_vnode(vp);
145 } else
146 r = err_code;
147 if (r != OK) {
148 unlock_vnode(dirp);
149 unlock_vmnt(vmp);
150 put_vnode(dirp);
151 return(r);
155 upgrade_vmnt_lock(vmp);
157 if (job_call_nr == VFS_UNLINK)
158 r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
159 else
160 r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
161 unlock_vnode(dirp);
162 unlock_vmnt(vmp);
163 put_vnode(dirp);
164 return(r);
167 /*===========================================================================*
168 * do_rename *
169 *===========================================================================*/
170 int do_rename(void)
172 /* Perform the rename(name1, name2) system call. */
173 int r = OK, r1;
174 struct vnode *old_dirp = NULL, *new_dirp = NULL, *new_dirp_l = NULL, *vp;
175 struct vmnt *oldvmp, *newvmp, *vmp2;
176 char old_name[PATH_MAX];
177 char fullpath[PATH_MAX];
178 struct lookup resolve, stickycheck;
179 vir_bytes vname1, vname2;
180 size_t vname1_length, vname2_length;
182 vname1 = job_m_in.m_lc_vfs_link.name1;
183 vname1_length = job_m_in.m_lc_vfs_link.len1;
184 vname2 = job_m_in.m_lc_vfs_link.name2;
185 vname2_length = job_m_in.m_lc_vfs_link.len2;
187 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &oldvmp, &old_dirp);
188 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
189 resolve.l_vmnt_lock = VMNT_WRITE;
190 resolve.l_vnode_lock = VNODE_WRITE;
192 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
193 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
194 if ((old_dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
196 /* If the sticky bit is set, only the owner of the file or a privileged
197 user is allowed to rename */
198 if ((old_dirp->v_mode & S_ISVTX) == S_ISVTX) {
199 /* Look up inode of file to unlink to retrieve owner */
200 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
201 stickycheck.l_vmnt_lock = VMNT_READ;
202 stickycheck.l_vnode_lock = VNODE_READ;
203 vp = advance(old_dirp, &stickycheck, fp);
204 assert(vmp2 == NULL);
205 if (vp != NULL) {
206 if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
207 r = EPERM;
208 unlock_vnode(vp);
209 put_vnode(vp);
210 } else
211 r = err_code;
212 if (r != OK) {
213 unlock_vnode(old_dirp);
214 unlock_vmnt(oldvmp);
215 put_vnode(old_dirp);
216 return(r);
220 /* Save the last component of the old name */
221 if (strlen(fullpath) >= sizeof(old_name)) {
222 unlock_vnode(old_dirp);
223 unlock_vmnt(oldvmp);
224 put_vnode(old_dirp);
225 return(ENAMETOOLONG);
227 strlcpy(old_name, fullpath, PATH_MAX);
229 /* See if 'name2' (new name) exists. Get dir inode */
230 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &newvmp, &new_dirp_l);
231 resolve.l_vmnt_lock = VMNT_READ;
232 resolve.l_vnode_lock = VNODE_WRITE;
233 if (fetch_name(vname2, vname2_length, fullpath) != OK) r = err_code;
234 else if ((new_dirp = last_dir(&resolve, fp)) == NULL) r = err_code;
236 /* We used a separate vnode pointer to see whether we obtained a lock on the
237 * new_dirp vnode. If the new directory and old directory are the same, then
238 * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will
239 * be NULL, but new_dirp will not.
241 if (new_dirp == old_dirp) assert(new_dirp_l == NULL);
243 if (r != OK) {
244 unlock_vnode(old_dirp);
245 unlock_vmnt(oldvmp);
246 put_vnode(old_dirp);
247 return(r);
250 /* Both parent directories must be on the same device. */
251 if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
253 /* Parent dirs must be writable, searchable and on a writable device */
254 if ((r1 = forbidden(fp, old_dirp, W_BIT|X_BIT)) != OK ||
255 (r1 = forbidden(fp, new_dirp, W_BIT|X_BIT)) != OK) r = r1;
257 if (r == OK) {
258 upgrade_vmnt_lock(oldvmp); /* Upgrade to exclusive access */
259 r = req_rename(old_dirp->v_fs_e, old_dirp->v_inode_nr, old_name,
260 new_dirp->v_inode_nr, fullpath);
263 unlock_vnode(old_dirp);
264 unlock_vmnt(oldvmp);
265 if (new_dirp_l) unlock_vnode(new_dirp_l);
266 if (newvmp) unlock_vmnt(newvmp);
268 put_vnode(old_dirp);
269 put_vnode(new_dirp);
271 return(r);
274 /*===========================================================================*
275 * do_truncate *
276 *===========================================================================*/
277 int do_truncate(void)
279 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
280 * do_truncate() and do_ftruncate() have to get hold of the inode, either
281 * by name or fd, do checks on it, and call truncate_inode() to do the
282 * work.
284 struct vnode *vp;
285 struct vmnt *vmp;
286 int r;
287 char fullpath[PATH_MAX];
288 struct lookup resolve;
289 off_t length;
290 vir_bytes vname;
291 size_t vname_length;
293 vname = job_m_in.m_lc_vfs_truncate.name;
294 vname_length = job_m_in.m_lc_vfs_truncate.len;
296 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
297 resolve.l_vmnt_lock = VMNT_READ;
298 resolve.l_vnode_lock = VNODE_WRITE;
300 length = job_m_in.m_lc_vfs_truncate.offset;
301 if (length < 0) return(EINVAL);
303 /* Temporarily open file */
304 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
305 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
307 /* Ask FS to truncate the file */
308 if ((r = forbidden(fp, vp, W_BIT)) == OK) {
309 /* If the file size does not change, do not make the actual call. This
310 * ensures that the file times are retained when the file size remains
311 * the same, which is a POSIX requirement.
313 if (S_ISREG(vp->v_mode) && vp->v_size == length)
314 r = OK;
315 else
316 r = truncate_vnode(vp, length);
319 unlock_vnode(vp);
320 unlock_vmnt(vmp);
321 put_vnode(vp);
322 return(r);
325 /*===========================================================================*
326 * do_ftruncate *
327 *===========================================================================*/
328 int do_ftruncate(void)
330 /* As with do_truncate(), truncate_vnode() does the actual work. */
331 struct filp *rfilp;
332 struct vnode *vp;
333 int r;
334 off_t length;
336 scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_truncate.fd;
338 length = job_m_in.m_lc_vfs_truncate.offset;
339 if (length < 0) return(EINVAL);
341 /* File is already opened; get a vnode pointer from filp */
342 if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
343 return(err_code);
345 vp = rfilp->filp_vno;
347 if (!(rfilp->filp_mode & W_BIT))
348 r = EBADF;
349 else if (S_ISREG(vp->v_mode) && vp->v_size == length)
350 /* If the file size does not change, do not make the actual call. This
351 * ensures that the file times are retained when the file size remains
352 * the same, which is a POSIX requirement.
354 r = OK;
355 else
356 r = truncate_vnode(vp, length);
358 unlock_filp(rfilp);
359 return(r);
363 /*===========================================================================*
364 * truncate_vnode *
365 *===========================================================================*/
366 int truncate_vnode(vp, newsize)
367 struct vnode *vp;
368 off_t newsize;
370 /* Truncate a regular file or a pipe */
371 int r;
373 assert(tll_locked_by_me(&vp->v_lock));
374 if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
376 /* We must not compare the old and the new size here: this function may be
377 * called for open(2), which requires an update to the file times if O_TRUNC
378 * is given, even if the file size remains the same.
380 if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
381 vp->v_size = newsize;
382 return(r);
386 /*===========================================================================*
387 * do_slink *
388 *===========================================================================*/
389 int do_slink(void)
391 /* Perform the symlink(name1, name2) system call. */
392 int r;
393 struct vnode *vp;
394 struct vmnt *vmp;
395 char fullpath[PATH_MAX];
396 struct lookup resolve;
397 vir_bytes vname1, vname2;
398 size_t vname1_length, vname2_length;
400 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
401 resolve.l_vmnt_lock = VMNT_WRITE;
402 resolve.l_vnode_lock = VNODE_WRITE;
404 vname1 = job_m_in.m_lc_vfs_link.name1;
405 vname1_length = job_m_in.m_lc_vfs_link.len1;
406 vname2 = job_m_in.m_lc_vfs_link.name2;
407 vname2_length = job_m_in.m_lc_vfs_link.len2;
409 if (vname1_length <= 1) return(ENOENT);
410 if (vname1_length >= _POSIX_SYMLINK_MAX) return(ENAMETOOLONG);
412 /* Get dir inode of 'name2' */
413 if (fetch_name(vname2, vname2_length, fullpath) != OK) return(err_code);
414 if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
415 if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
416 r = req_slink(vp->v_fs_e, vp->v_inode_nr, fullpath, who_e,
417 vname1, vname1_length - 1, fp->fp_effuid,
418 fp->fp_effgid);
421 unlock_vnode(vp);
422 unlock_vmnt(vmp);
423 put_vnode(vp);
425 return(r);
428 /*===========================================================================*
429 * rdlink_direct *
430 *===========================================================================*/
431 int rdlink_direct(orig_path, link_path, rfp)
432 char *orig_path;
433 char link_path[PATH_MAX]; /* should have length PATH_MAX */
434 struct fproc *rfp;
436 /* Perform a readlink()-like call from within the VFS */
437 int r;
438 struct vnode *vp;
439 struct vmnt *vmp;
440 struct lookup resolve;
442 lookup_init(&resolve, link_path, PATH_RET_SYMLINK, &vmp, &vp);
443 resolve.l_vmnt_lock = VMNT_READ;
444 resolve.l_vnode_lock = VNODE_READ;
446 /* Temporarily open the file containing the symbolic link. Use link_path
447 * for temporary storage to keep orig_path untouched. */
448 strncpy(link_path, orig_path, PATH_MAX); /* PATH_MAX includes '\0' */
449 link_path[PATH_MAX - 1] = '\0';
450 if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
452 /* Make sure this is a symbolic link */
453 if (!S_ISLNK(vp->v_mode))
454 r = EINVAL;
455 else
456 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path,
457 PATH_MAX - 1, 1);
459 if (r > 0) link_path[r] = '\0'; /* Terminate string when succesful */
461 unlock_vnode(vp);
462 unlock_vmnt(vmp);
463 put_vnode(vp);
465 return r;
468 /*===========================================================================*
469 * do_rdlink *
470 *===========================================================================*/
471 int do_rdlink(void)
473 /* Perform the readlink(name, buf, bufsize) system call. */
474 int r;
475 struct vnode *vp;
476 struct vmnt *vmp;
477 char fullpath[PATH_MAX];
478 struct lookup resolve;
479 vir_bytes vname;
480 size_t vname_length, buf_size;
481 vir_bytes buf;
483 vname = job_m_in.m_lc_vfs_readlink.name;
484 vname_length = job_m_in.m_lc_vfs_readlink.namelen;
485 buf = job_m_in.m_lc_vfs_readlink.buf;
486 buf_size = job_m_in.m_lc_vfs_readlink.bufsize;
487 if (buf_size > SSIZE_MAX) return(EINVAL);
489 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
490 resolve.l_vmnt_lock = VMNT_READ;
491 resolve.l_vnode_lock = VNODE_READ;
493 /* Temporarily open the file containing the symbolic link */
494 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
495 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
497 /* Make sure this is a symbolic link */
498 if (!S_ISLNK(vp->v_mode))
499 r = EINVAL;
500 else
501 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0);
503 unlock_vnode(vp);
504 unlock_vmnt(vmp);
505 put_vnode(vp);
507 return(r);