vm: restore stacktrace on SIGSEGV
[minix.git] / servers / vfs / link.c
blob6a025ccc77f53ef01602a7acc16beb3dc5dae076
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_READ;
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, *vp;
102 struct vmnt *vmp, *vmp2;
103 int r;
104 char fullpath[PATH_MAX];
105 struct lookup resolve;
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);
118 resolve.l_vmnt_lock = VMNT_WRITE;
119 resolve.l_vnode_lock = VNODE_READ;
121 /* Get the last directory in the path. */
122 if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
123 assert(vmp != NULL); /* We must have locked the vmnt */
125 /* Make sure that the object is a directory */
126 if (!S_ISDIR(dirp->v_mode)) {
127 unlock_vnode(dirp);
128 unlock_vmnt(vmp);
129 put_vnode(dirp);
130 return(ENOTDIR);
133 /* The caller must have both search and execute permission */
134 if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
135 unlock_vnode(dirp);
136 unlock_vmnt(vmp);
137 put_vnode(dirp);
138 return(r);
141 /* Also, if the sticky bit is set, only the owner of the file or a privileged
142 user is allowed to unlink */
143 if ((dirp->v_mode & S_ISVTX) == S_ISVTX) {
144 /* Look up inode of file to unlink to retrieve owner */
145 resolve.l_flags = PATH_RET_SYMLINK;
146 resolve.l_vmp = &vmp2; /* Shouldn't actually get locked */
147 resolve.l_vmnt_lock = VMNT_READ;
148 resolve.l_vnode = &vp;
149 resolve.l_vnode_lock = VNODE_READ;
150 vp = advance(dirp, &resolve, fp);
151 assert(vmp2 == NULL);
152 if (vp != NULL) {
153 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
154 r = EPERM;
155 unlock_vnode(vp);
156 put_vnode(vp);
157 } else
158 r = err_code;
159 if (r != OK) {
160 unlock_vnode(dirp);
161 unlock_vmnt(vmp);
162 put_vnode(dirp);
163 return(r);
167 assert(vmp != NULL);
168 tll_upgrade(&vmp->m_lock);
170 if (job_call_nr == UNLINK)
171 r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
172 else
173 r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
174 unlock_vnode(dirp);
175 unlock_vmnt(vmp);
176 put_vnode(dirp);
177 return(r);
180 /*===========================================================================*
181 * do_rename *
182 *===========================================================================*/
183 int do_rename()
185 /* Perform the rename(name1, name2) system call. */
186 int r = OK, r1;
187 struct vnode *old_dirp, *new_dirp = NULL, *vp;
188 struct vmnt *oldvmp, *newvmp, *vmp2;
189 char old_name[PATH_MAX];
190 char fullpath[PATH_MAX];
191 struct lookup resolve;
192 vir_bytes vname1, vname2;
193 size_t vname1_length, vname2_length;
195 vname1 = (vir_bytes) job_m_in.name1;
196 vname1_length = job_m_in.name1_length;
197 vname2 = (vir_bytes) job_m_in.name2;
198 vname2_length = job_m_in.name2_length;
200 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &oldvmp, &old_dirp);
201 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
202 resolve.l_vmnt_lock = VMNT_WRITE;
203 resolve.l_vnode_lock = VNODE_READ;
205 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
206 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
207 if ((old_dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
209 /* If the sticky bit is set, only the owner of the file or a privileged
210 user is allowed to rename */
211 if ((old_dirp->v_mode & S_ISVTX) == S_ISVTX) {
212 /* Look up inode of file to unlink to retrieve owner */
213 lookup_init(&resolve, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
214 resolve.l_vmnt_lock = VMNT_READ;
215 resolve.l_vnode_lock = VNODE_READ;
216 vp = advance(old_dirp, &resolve, fp);
217 assert(vmp2 == NULL);
218 if (vp != NULL) {
219 if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
220 r = EPERM;
221 unlock_vnode(vp);
222 put_vnode(vp);
223 } else
224 r = err_code;
225 if (r != OK) {
226 unlock_vnode(old_dirp);
227 unlock_vmnt(oldvmp);
228 put_vnode(old_dirp);
229 return(r);
233 /* Save the last component of the old name */
234 if (strlen(fullpath) >= sizeof(old_name)) {
235 unlock_vnode(old_dirp);
236 unlock_vmnt(oldvmp);
237 put_vnode(old_dirp);
238 return(ENAMETOOLONG);
240 strlcpy(old_name, fullpath, PATH_MAX);
242 /* See if 'name2' (new name) exists. Get dir inode */
243 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &newvmp, &new_dirp);
244 resolve.l_vmnt_lock = VMNT_READ;
245 resolve.l_vnode_lock = VNODE_READ;
246 if (fetch_name(vname2, vname2_length, fullpath) != OK) r = err_code;
247 else if ((new_dirp = last_dir(&resolve, fp)) == NULL) r = err_code;
249 if (r != OK) {
250 unlock_vnode(old_dirp);
251 unlock_vmnt(oldvmp);
252 put_vnode(old_dirp);
253 return(r);
256 /* Both parent directories must be on the same device. */
257 if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
259 /* Parent dirs must be writable, searchable and on a writable device */
260 if ((r1 = forbidden(fp, old_dirp, W_BIT|X_BIT)) != OK ||
261 (r1 = forbidden(fp, new_dirp, W_BIT|X_BIT)) != OK) r = r1;
263 if (r == OK) {
264 tll_upgrade(&oldvmp->m_lock); /* Upgrade to exclusive access */
265 r = req_rename(old_dirp->v_fs_e, old_dirp->v_inode_nr, old_name,
266 new_dirp->v_inode_nr, fullpath);
268 unlock_vnode(old_dirp);
269 unlock_vnode(new_dirp);
270 unlock_vmnt(oldvmp);
271 if (newvmp) unlock_vmnt(newvmp);
273 put_vnode(old_dirp);
274 put_vnode(new_dirp);
276 return(r);
279 /*===========================================================================*
280 * do_truncate *
281 *===========================================================================*/
282 int do_truncate()
284 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
285 * do_truncate() and do_ftruncate() have to get hold of the inode, either
286 * by name or fd, do checks on it, and call truncate_inode() to do the
287 * work.
289 struct vnode *vp;
290 struct vmnt *vmp;
291 int r;
292 char fullpath[PATH_MAX];
293 struct lookup resolve;
294 off_t length;
295 vir_bytes vname;
296 size_t vname_length;
298 vname = (vir_bytes) job_m_in.m2_p1;
299 vname_length = job_m_in.m2_i1;
301 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
302 resolve.l_vmnt_lock = VMNT_EXCL;
303 resolve.l_vnode_lock = VNODE_WRITE;
305 length = (off_t) job_m_in.flength;
306 if (length < 0) return(EINVAL);
308 /* Temporarily open file */
309 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
310 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
312 /* Ask FS to truncate the file */
313 if ((r = forbidden(fp, vp, W_BIT)) == OK) {
314 /* If the file size does not change, do not make the actual call. This
315 * ensures that the file times are retained when the file size remains
316 * the same, which is a POSIX requirement.
318 if (S_ISREG(vp->v_mode) && vp->v_size == length)
319 r = OK;
320 else
321 r = truncate_vnode(vp, length);
324 unlock_vnode(vp);
325 unlock_vmnt(vmp);
326 put_vnode(vp);
327 return(r);
330 /*===========================================================================*
331 * do_ftruncate *
332 *===========================================================================*/
333 int do_ftruncate()
335 /* As with do_truncate(), truncate_vnode() does the actual work. */
336 struct filp *rfilp;
337 struct vnode *vp;
338 int r;
339 off_t length;
341 scratch(fp).file.fd_nr = job_m_in.fd;
342 length = (off_t) job_m_in.flength;
344 if (length < 0) return(EINVAL);
346 /* File is already opened; get a vnode pointer from filp */
347 if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
348 return(err_code);
350 vp = rfilp->filp_vno;
352 if (!(rfilp->filp_mode & W_BIT))
353 r = EBADF;
354 else if (S_ISREG(vp->v_mode) && vp->v_size == length)
355 /* If the file size does not change, do not make the actual call. This
356 * ensures that the file times are retained when the file size remains
357 * the same, which is a POSIX requirement.
359 r = OK;
360 else
361 r = truncate_vnode(vp, length);
363 unlock_filp(rfilp);
364 return(r);
368 /*===========================================================================*
369 * truncate_vnode *
370 *===========================================================================*/
371 int truncate_vnode(vp, newsize)
372 struct vnode *vp;
373 off_t newsize;
375 /* Truncate a regular file or a pipe */
376 int r;
378 assert(tll_locked_by_me(&vp->v_lock));
379 if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
381 /* We must not compare the old and the new size here: this function may be
382 * called for open(2), which requires an update to the file times if O_TRUNC
383 * is given, even if the file size remains the same.
385 if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
386 vp->v_size = newsize;
387 return(r);
391 /*===========================================================================*
392 * do_slink *
393 *===========================================================================*/
394 int do_slink()
396 /* Perform the symlink(name1, name2) system call. */
397 int r;
398 struct vnode *vp;
399 struct vmnt *vmp;
400 char fullpath[PATH_MAX];
401 struct lookup resolve;
402 vir_bytes vname1, vname2;
403 size_t vname1_length, vname2_length;
405 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
406 resolve.l_vmnt_lock = VMNT_WRITE;
407 resolve.l_vnode_lock = VNODE_READ;
409 vname1 = (vir_bytes) job_m_in.name1;
410 vname1_length = job_m_in.name1_length;
411 vname2 = (vir_bytes) job_m_in.name2;
412 vname2_length = job_m_in.name2_length;
414 if (vname1_length <= 1) return(ENOENT);
415 if (vname1_length >= SYMLINK_MAX) return(ENAMETOOLONG);
417 /* Get dir inode of 'name2' */
418 if (fetch_name(vname2, vname2_length, fullpath) != OK) return(err_code);
419 if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
420 if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
421 r = req_slink(vp->v_fs_e, vp->v_inode_nr, fullpath, who_e,
422 vname1, vname1_length - 1, fp->fp_effuid,
423 fp->fp_effgid);
426 unlock_vnode(vp);
427 unlock_vmnt(vmp);
428 put_vnode(vp);
430 return(r);
433 /*===========================================================================*
434 * rdlink_direct *
435 *===========================================================================*/
436 int rdlink_direct(orig_path, link_path, rfp)
437 char *orig_path;
438 char link_path[PATH_MAX]; /* should have length PATH_MAX */
439 struct fproc *rfp;
441 /* Perform a readlink()-like call from within the VFS */
442 int r;
443 struct vnode *vp;
444 struct vmnt *vmp;
445 struct lookup resolve;
447 lookup_init(&resolve, link_path, PATH_RET_SYMLINK, &vmp, &vp);
448 resolve.l_vmnt_lock = VMNT_READ;
449 resolve.l_vnode_lock = VNODE_READ;
451 /* Temporarily open the file containing the symbolic link. Use link_path
452 * for temporary storage to keep orig_path untouched. */
453 strncpy(link_path, orig_path, PATH_MAX); /* PATH_MAX includes '\0' */
454 link_path[PATH_MAX - 1] = '\0';
455 if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
457 /* Make sure this is a symbolic link */
458 if (!S_ISLNK(vp->v_mode))
459 r = EINVAL;
460 else
461 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path,
462 PATH_MAX - 1, 1);
464 if (r > 0) link_path[r] = '\0'; /* Terminate string when succesful */
466 unlock_vnode(vp);
467 unlock_vmnt(vmp);
468 put_vnode(vp);
470 return r;
473 /*===========================================================================*
474 * do_rdlink *
475 *===========================================================================*/
476 int do_rdlink()
478 /* Perform the readlink(name, buf, bufsize) system call. */
479 int r;
480 struct vnode *vp;
481 struct vmnt *vmp;
482 char fullpath[PATH_MAX];
483 struct lookup resolve;
484 vir_bytes vname;
485 size_t vname_length, buf_size;
486 vir_bytes buf;
488 vname = (vir_bytes) job_m_in.name1;
489 vname_length = job_m_in.name1_length;
490 buf = (vir_bytes) job_m_in.name2;
491 buf_size = (size_t) job_m_in.nbytes;
492 if (buf_size > SSIZE_MAX) return(EINVAL);
494 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
495 resolve.l_vmnt_lock = VMNT_READ;
496 resolve.l_vnode_lock = VNODE_READ;
498 /* Temporarily open the file containing the symbolic link */
499 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
500 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
502 /* Make sure this is a symbolic link */
503 if (!S_ISLNK(vp->v_mode))
504 r = EINVAL;
505 else
506 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0);
508 unlock_vnode(vp);
509 unlock_vmnt(vmp);
510 put_vnode(vp);
512 return(r);