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>
25 #include "scratchpad.h"
27 /*===========================================================================*
29 *===========================================================================*/
32 /* Perform the link(name1, name2) system call. */
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
)
60 else if ((dirp
= last_dir(&resolve
, fp
)) == NULL
)
70 /* Check for links across devices. */
71 if (vp
->v_fs_e
!= dirp
->v_fs_e
)
74 r
= forbidden(fp
, dirp
, W_BIT
| X_BIT
);
77 r
= req_link(vp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
,
82 if (vmp2
!= NULL
) unlock_vmnt(vmp2
);
89 /*===========================================================================*
91 *===========================================================================*/
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
;
102 char fullpath
[PATH_MAX
];
103 struct lookup resolve
, stickycheck
;
105 if (copy_path(fullpath
, sizeof(fullpath
)) != OK
)
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
)) {
123 /* The caller must have both search and execute permission */
124 if ((r
= forbidden(fp
, dirp
, X_BIT
| W_BIT
)) != OK
) {
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
);
141 if (vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
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
);
160 r
= req_rmdir(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
167 /*===========================================================================*
169 *===========================================================================*/
172 /* Perform the rename(name1, name2) system call. */
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
);
206 if(vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
213 unlock_vnode(old_dirp
);
220 /* Save the last component of the old name */
221 if (strlen(fullpath
) >= sizeof(old_name
)) {
222 unlock_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
);
244 unlock_vnode(old_dirp
);
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
;
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
);
265 if (new_dirp_l
) unlock_vnode(new_dirp_l
);
266 if (newvmp
) unlock_vmnt(newvmp
);
274 /*===========================================================================*
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
287 char fullpath
[PATH_MAX
];
288 struct lookup resolve
;
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
)
316 r
= truncate_vnode(vp
, length
);
325 /*===========================================================================*
327 *===========================================================================*/
328 int do_ftruncate(void)
330 /* As with do_truncate(), truncate_vnode() does the actual work. */
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
)
345 vp
= rfilp
->filp_vno
;
347 if (!(rfilp
->filp_mode
& W_BIT
))
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.
356 r
= truncate_vnode(vp
, length
);
363 /*===========================================================================*
365 *===========================================================================*/
366 int truncate_vnode(vp
, newsize
)
370 /* Truncate a regular file or a pipe */
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
;
386 /*===========================================================================*
388 *===========================================================================*/
391 /* Perform the symlink(name1, name2) system call. */
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
,
428 /*===========================================================================*
430 *===========================================================================*/
431 int rdlink_direct(orig_path
, link_path
, rfp
)
433 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);