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
17 #include <minix/com.h>
18 #include <minix/callnr.h>
19 #include <minix/vfsif.h>
27 #include "scratchpad.h"
29 /*===========================================================================*
31 *===========================================================================*/
34 /* Perform the link(name1, name2) system call. */
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
)
62 else if ((dirp
= last_dir(&resolve
, fp
)) == NULL
)
72 /* Check for links across devices. */
73 if (vp
->v_fs_e
!= dirp
->v_fs_e
)
76 r
= forbidden(fp
, dirp
, W_BIT
| X_BIT
);
79 r
= req_link(vp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
,
84 if (vmp2
!= NULL
) unlock_vmnt(vmp2
);
91 /*===========================================================================*
93 *===========================================================================*/
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
;
104 char fullpath
[PATH_MAX
];
105 struct lookup resolve
;
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
)
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
)) {
133 /* The caller must have both search and execute permission */
134 if ((r
= forbidden(fp
, dirp
, X_BIT
| W_BIT
)) != OK
) {
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
);
153 if (vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
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
);
173 r
= req_rmdir(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
180 /*===========================================================================*
182 *===========================================================================*/
185 /* Perform the rename(name1, name2) system call. */
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
);
219 if(vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
226 unlock_vnode(old_dirp
);
233 /* Save the last component of the old name */
234 if (strlen(fullpath
) >= sizeof(old_name
)) {
235 unlock_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
;
250 unlock_vnode(old_dirp
);
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
;
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
);
271 if (newvmp
) unlock_vmnt(newvmp
);
279 /*===========================================================================*
281 *===========================================================================*/
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
292 char fullpath
[PATH_MAX
];
293 struct lookup resolve
;
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
)
321 r
= truncate_vnode(vp
, length
);
330 /*===========================================================================*
332 *===========================================================================*/
335 /* As with do_truncate(), truncate_vnode() does the actual work. */
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
)
350 vp
= rfilp
->filp_vno
;
352 if (!(rfilp
->filp_mode
& W_BIT
))
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.
361 r
= truncate_vnode(vp
, length
);
368 /*===========================================================================*
370 *===========================================================================*/
371 int truncate_vnode(vp
, newsize
)
375 /* Truncate a regular file or a pipe */
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
;
391 /*===========================================================================*
393 *===========================================================================*/
396 /* Perform the symlink(name1, name2) system call. */
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
,
433 /*===========================================================================*
435 *===========================================================================*/
436 int rdlink_direct(orig_path
, link_path
, rfp
)
438 char link_path
[PATH_MAX
]; /* should have length PATH_MAX */
441 /* Perform a readlink()-like call from within the VFS */
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
))
461 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, NONE
, (vir_bytes
) link_path
,
464 if (r
> 0) link_path
[r
] = '\0'; /* Terminate string when succesful */
473 /*===========================================================================*
475 *===========================================================================*/
478 /* Perform the readlink(name, buf, bufsize) system call. */
482 char fullpath
[PATH_MAX
];
483 struct lookup resolve
;
485 size_t vname_length
, buf_size
;
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
))
506 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, who_e
, buf
, buf_size
, 0);