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>
20 #include <sys/dirent.h>
26 /*===========================================================================*
28 *===========================================================================*/
31 /* Perform the link(name1, name2) system call. */
33 struct vnode
*vp
= NULL
, *dirp
= NULL
;
34 struct vmnt
*vmp1
= NULL
, *vmp2
= NULL
;
35 char fullpath
[PATH_MAX
];
36 struct lookup resolve
;
37 vir_bytes vname1
, vname2
;
38 size_t vname1_length
, vname2_length
;
40 vname1
= job_m_in
.m_lc_vfs_link
.name1
;
41 vname1_length
= job_m_in
.m_lc_vfs_link
.len1
;
42 vname2
= job_m_in
.m_lc_vfs_link
.name2
;
43 vname2_length
= job_m_in
.m_lc_vfs_link
.len2
;
45 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp1
, &vp
);
46 resolve
.l_vmnt_lock
= VMNT_WRITE
;
47 resolve
.l_vnode_lock
= VNODE_READ
;
49 /* See if 'name1' (file to be linked to) exists. */
50 if (fetch_name(vname1
, vname1_length
, fullpath
) != OK
) return(err_code
);
51 if ((vp
= eat_path(&resolve
, fp
)) == NULL
) return(err_code
);
53 /* Does the final directory of 'name2' exist? */
54 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp2
, &dirp
);
55 resolve
.l_vmnt_lock
= VMNT_READ
;
56 resolve
.l_vnode_lock
= VNODE_WRITE
;
57 if (fetch_name(vname2
, vname2_length
, fullpath
) != OK
)
59 else if ((dirp
= last_dir(&resolve
, fp
)) == NULL
)
69 /* Check for links across devices. */
70 if (vp
->v_fs_e
!= dirp
->v_fs_e
)
73 r
= forbidden(fp
, dirp
, W_BIT
| X_BIT
);
76 r
= req_link(vp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
,
81 if (vmp2
!= NULL
) unlock_vmnt(vmp2
);
88 /*===========================================================================*
90 *===========================================================================*/
93 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
94 * is almost the same. They differ only in some condition testing. Unlink()
95 * may be used by the superuser to do dangerous things; rmdir() may not.
96 * The syscall might provide 'name' embedded in the message.
98 struct vnode
*dirp
, *dirp_l
, *vp
;
99 struct vmnt
*vmp
, *vmp2
;
101 char fullpath
[PATH_MAX
];
102 struct lookup resolve
, stickycheck
;
104 if (copy_path(fullpath
, sizeof(fullpath
)) != OK
)
107 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &vmp
, &dirp_l
);
108 resolve
.l_vmnt_lock
= VMNT_WRITE
;
109 resolve
.l_vnode_lock
= VNODE_WRITE
;
111 /* Get the last directory in the path. */
112 if ((dirp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
114 /* Make sure that the object is a directory */
115 if (!S_ISDIR(dirp
->v_mode
)) {
122 /* The caller must have both search and execute permission */
123 if ((r
= forbidden(fp
, dirp
, X_BIT
| W_BIT
)) != OK
) {
130 /* Also, if the sticky bit is set, only the owner of the file or a privileged
131 user is allowed to unlink */
132 if ((dirp
->v_mode
& S_ISVTX
) == S_ISVTX
) {
133 /* Look up inode of file to unlink to retrieve owner */
134 lookup_init(&stickycheck
, resolve
.l_path
, PATH_RET_SYMLINK
, &vmp2
, &vp
);
135 stickycheck
.l_vmnt_lock
= VMNT_READ
;
136 stickycheck
.l_vnode_lock
= VNODE_READ
;
137 vp
= advance(dirp
, &stickycheck
, fp
);
138 assert(vmp2
== NULL
);
140 if (vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
154 upgrade_vmnt_lock(vmp
);
156 if (job_call_nr
== VFS_UNLINK
)
157 r
= req_unlink(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
159 r
= req_rmdir(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
166 /*===========================================================================*
168 *===========================================================================*/
171 /* Perform the rename(name1, name2) system call. */
173 struct vnode
*old_dirp
= NULL
, *new_dirp
= NULL
, *new_dirp_l
= NULL
, *vp
;
174 struct vmnt
*oldvmp
, *newvmp
, *vmp2
;
175 char old_name
[PATH_MAX
];
176 char fullpath
[PATH_MAX
];
177 struct lookup resolve
, stickycheck
;
178 vir_bytes vname1
, vname2
;
179 size_t vname1_length
, vname2_length
;
181 vname1
= job_m_in
.m_lc_vfs_link
.name1
;
182 vname1_length
= job_m_in
.m_lc_vfs_link
.len1
;
183 vname2
= job_m_in
.m_lc_vfs_link
.name2
;
184 vname2_length
= job_m_in
.m_lc_vfs_link
.len2
;
186 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &oldvmp
, &old_dirp
);
187 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
188 resolve
.l_vmnt_lock
= VMNT_WRITE
;
189 resolve
.l_vnode_lock
= VNODE_WRITE
;
191 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
192 if (fetch_name(vname1
, vname1_length
, fullpath
) != OK
) return(err_code
);
193 if ((old_dirp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
195 /* If the sticky bit is set, only the owner of the file or a privileged
196 user is allowed to rename */
197 if ((old_dirp
->v_mode
& S_ISVTX
) == S_ISVTX
) {
198 /* Look up inode of file to unlink to retrieve owner */
199 lookup_init(&stickycheck
, resolve
.l_path
, PATH_RET_SYMLINK
, &vmp2
, &vp
);
200 stickycheck
.l_vmnt_lock
= VMNT_READ
;
201 stickycheck
.l_vnode_lock
= VNODE_READ
;
202 vp
= advance(old_dirp
, &stickycheck
, fp
);
203 assert(vmp2
== NULL
);
205 if(vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
212 unlock_vnode(old_dirp
);
219 /* Save the last component of the old name */
220 if (strlen(fullpath
) >= sizeof(old_name
)) {
221 unlock_vnode(old_dirp
);
224 return(ENAMETOOLONG
);
226 strlcpy(old_name
, fullpath
, PATH_MAX
);
228 /* See if 'name2' (new name) exists. Get dir inode */
229 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &newvmp
, &new_dirp_l
);
230 resolve
.l_vmnt_lock
= VMNT_READ
;
231 resolve
.l_vnode_lock
= VNODE_WRITE
;
232 if (fetch_name(vname2
, vname2_length
, fullpath
) != OK
) r
= err_code
;
233 else if ((new_dirp
= last_dir(&resolve
, fp
)) == NULL
) r
= err_code
;
235 /* We used a separate vnode pointer to see whether we obtained a lock on the
236 * new_dirp vnode. If the new directory and old directory are the same, then
237 * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will
238 * be NULL, but new_dirp will not.
240 if (new_dirp
== old_dirp
) assert(new_dirp_l
== NULL
);
243 unlock_vnode(old_dirp
);
249 /* Both parent directories must be on the same device. */
250 if (old_dirp
->v_fs_e
!= new_dirp
->v_fs_e
) r
= EXDEV
;
252 /* Parent dirs must be writable, searchable and on a writable device */
253 if ((r1
= forbidden(fp
, old_dirp
, W_BIT
|X_BIT
)) != OK
||
254 (r1
= forbidden(fp
, new_dirp
, W_BIT
|X_BIT
)) != OK
) r
= r1
;
257 upgrade_vmnt_lock(oldvmp
); /* Upgrade to exclusive access */
258 r
= req_rename(old_dirp
->v_fs_e
, old_dirp
->v_inode_nr
, old_name
,
259 new_dirp
->v_inode_nr
, fullpath
);
262 unlock_vnode(old_dirp
);
264 if (new_dirp_l
) unlock_vnode(new_dirp_l
);
265 if (newvmp
) unlock_vmnt(newvmp
);
273 /*===========================================================================*
275 *===========================================================================*/
276 int do_truncate(void)
278 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
279 * do_truncate() and do_ftruncate() have to get hold of the inode, either
280 * by name or fd, do checks on it, and call truncate_inode() to do the
286 char fullpath
[PATH_MAX
];
287 struct lookup resolve
;
292 vname
= job_m_in
.m_lc_vfs_truncate
.name
;
293 vname_length
= job_m_in
.m_lc_vfs_truncate
.len
;
295 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp
, &vp
);
296 resolve
.l_vmnt_lock
= VMNT_READ
;
297 resolve
.l_vnode_lock
= VNODE_WRITE
;
299 length
= job_m_in
.m_lc_vfs_truncate
.offset
;
300 if (length
< 0) return(EINVAL
);
302 /* Temporarily open file */
303 if (fetch_name(vname
, vname_length
, fullpath
) != OK
) return(err_code
);
304 if ((vp
= eat_path(&resolve
, fp
)) == NULL
) return(err_code
);
306 /* Ask FS to truncate the file */
307 if ((r
= forbidden(fp
, vp
, W_BIT
)) == OK
) {
308 /* If the file size does not change, do not make the actual call. This
309 * ensures that the file times are retained when the file size remains
310 * the same, which is a POSIX requirement.
312 if (S_ISREG(vp
->v_mode
) && vp
->v_size
== length
)
315 r
= truncate_vnode(vp
, length
);
324 /*===========================================================================*
326 *===========================================================================*/
327 int do_ftruncate(void)
329 /* As with do_truncate(), truncate_vnode() does the actual work. */
335 fd
= job_m_in
.m_lc_vfs_truncate
.fd
;
337 length
= job_m_in
.m_lc_vfs_truncate
.offset
;
338 if (length
< 0) return(EINVAL
);
340 /* File is already opened; get a vnode pointer from filp */
341 if ((rfilp
= get_filp(fd
, VNODE_WRITE
)) == NULL
)
344 vp
= rfilp
->filp_vno
;
346 if (!(rfilp
->filp_mode
& W_BIT
))
348 else if (S_ISREG(vp
->v_mode
) && vp
->v_size
== length
)
349 /* If the file size does not change, do not make the actual call. This
350 * ensures that the file times are retained when the file size remains
351 * the same, which is a POSIX requirement.
355 r
= truncate_vnode(vp
, length
);
362 /*===========================================================================*
364 *===========================================================================*/
366 truncate_vnode(struct vnode
*vp
, off_t newsize
)
368 /* Truncate a regular file or a pipe */
371 assert(tll_locked_by_me(&vp
->v_lock
));
372 if (!S_ISREG(vp
->v_mode
) && !S_ISFIFO(vp
->v_mode
)) return(EINVAL
);
374 /* We must not compare the old and the new size here: this function may be
375 * called for open(2), which requires an update to the file times if O_TRUNC
376 * is given, even if the file size remains the same.
378 if ((r
= req_ftrunc(vp
->v_fs_e
, vp
->v_inode_nr
, newsize
, 0)) == OK
)
379 vp
->v_size
= newsize
;
384 /*===========================================================================*
386 *===========================================================================*/
389 /* Perform the symlink(name1, name2) system call. */
393 char fullpath
[PATH_MAX
];
394 struct lookup resolve
;
395 vir_bytes vname1
, vname2
;
396 size_t vname1_length
, vname2_length
;
398 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp
, &vp
);
399 resolve
.l_vmnt_lock
= VMNT_WRITE
;
400 resolve
.l_vnode_lock
= VNODE_WRITE
;
402 vname1
= job_m_in
.m_lc_vfs_link
.name1
;
403 vname1_length
= job_m_in
.m_lc_vfs_link
.len1
;
404 vname2
= job_m_in
.m_lc_vfs_link
.name2
;
405 vname2_length
= job_m_in
.m_lc_vfs_link
.len2
;
407 if (vname1_length
<= 1) return(ENOENT
);
408 if (vname1_length
>= _POSIX_SYMLINK_MAX
) return(ENAMETOOLONG
);
410 /* Get dir inode of 'name2' */
411 if (fetch_name(vname2
, vname2_length
, fullpath
) != OK
) return(err_code
);
412 if ((vp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
413 if ((r
= forbidden(fp
, vp
, W_BIT
|X_BIT
)) == OK
) {
414 r
= req_slink(vp
->v_fs_e
, vp
->v_inode_nr
, fullpath
, who_e
,
415 vname1
, vname1_length
- 1, fp
->fp_effuid
,
426 /*===========================================================================*
428 *===========================================================================*/
432 char link_path
[PATH_MAX
], /* should have length PATH_MAX */
436 /* Perform a readlink()-like call from within the VFS */
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
))
456 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, NONE
, (vir_bytes
) link_path
,
459 if (r
> 0) link_path
[r
] = '\0'; /* Terminate string when succesful */
468 /*===========================================================================*
470 *===========================================================================*/
473 /* Perform the readlink(name, buf, bufsize) system call. */
477 char fullpath
[PATH_MAX
];
478 struct lookup resolve
;
480 size_t vname_length
, buf_size
;
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
))
501 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, who_e
, buf
, buf_size
, 0);