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_WRITE
;
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
, *dirp_l
, *vp
;
102 struct vmnt
*vmp
, *vmp2
;
104 char fullpath
[PATH_MAX
];
105 struct lookup resolve
, stickycheck
;
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_l
);
118 resolve
.l_vmnt_lock
= VMNT_WRITE
;
119 resolve
.l_vnode_lock
= VNODE_WRITE
;
121 /* Get the last directory in the path. */
122 if ((dirp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
124 /* Make sure that the object is a directory */
125 if (!S_ISDIR(dirp
->v_mode
)) {
132 /* The caller must have both search and execute permission */
133 if ((r
= forbidden(fp
, dirp
, X_BIT
| W_BIT
)) != OK
) {
140 /* Also, if the sticky bit is set, only the owner of the file or a privileged
141 user is allowed to unlink */
142 if ((dirp
->v_mode
& S_ISVTX
) == S_ISVTX
) {
143 /* Look up inode of file to unlink to retrieve owner */
144 lookup_init(&stickycheck
, resolve
.l_path
, PATH_RET_SYMLINK
, &vmp2
, &vp
);
145 stickycheck
.l_vmnt_lock
= VMNT_READ
;
146 stickycheck
.l_vnode_lock
= VNODE_READ
;
147 vp
= advance(dirp
, &stickycheck
, fp
);
148 assert(vmp2
== NULL
);
150 if (vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
164 upgrade_vmnt_lock(vmp
);
166 if (job_call_nr
== UNLINK
)
167 r
= req_unlink(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
169 r
= req_rmdir(dirp
->v_fs_e
, dirp
->v_inode_nr
, fullpath
);
176 /*===========================================================================*
178 *===========================================================================*/
181 /* Perform the rename(name1, name2) system call. */
183 struct vnode
*old_dirp
= NULL
, *new_dirp
= NULL
, *new_dirp_l
= NULL
, *vp
;
184 struct vmnt
*oldvmp
, *newvmp
, *vmp2
;
185 char old_name
[PATH_MAX
];
186 char fullpath
[PATH_MAX
];
187 struct lookup resolve
, stickycheck
;
188 vir_bytes vname1
, vname2
;
189 size_t vname1_length
, vname2_length
;
191 vname1
= (vir_bytes
) job_m_in
.name1
;
192 vname1_length
= job_m_in
.name1_length
;
193 vname2
= (vir_bytes
) job_m_in
.name2
;
194 vname2_length
= job_m_in
.name2_length
;
196 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &oldvmp
, &old_dirp
);
197 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
198 resolve
.l_vmnt_lock
= VMNT_WRITE
;
199 resolve
.l_vnode_lock
= VNODE_WRITE
;
201 /* See if 'name1' (existing file) exists. Get dir and file inodes. */
202 if (fetch_name(vname1
, vname1_length
, fullpath
) != OK
) return(err_code
);
203 if ((old_dirp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
205 /* If the sticky bit is set, only the owner of the file or a privileged
206 user is allowed to rename */
207 if ((old_dirp
->v_mode
& S_ISVTX
) == S_ISVTX
) {
208 /* Look up inode of file to unlink to retrieve owner */
209 lookup_init(&stickycheck
, resolve
.l_path
, PATH_RET_SYMLINK
, &vmp2
, &vp
);
210 stickycheck
.l_vmnt_lock
= VMNT_READ
;
211 stickycheck
.l_vnode_lock
= VNODE_READ
;
212 vp
= advance(old_dirp
, &stickycheck
, fp
);
213 assert(vmp2
== NULL
);
215 if(vp
->v_uid
!= fp
->fp_effuid
&& fp
->fp_effuid
!= SU_UID
)
222 unlock_vnode(old_dirp
);
229 /* Save the last component of the old name */
230 if (strlen(fullpath
) >= sizeof(old_name
)) {
231 unlock_vnode(old_dirp
);
234 return(ENAMETOOLONG
);
236 strlcpy(old_name
, fullpath
, PATH_MAX
);
238 /* See if 'name2' (new name) exists. Get dir inode */
239 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &newvmp
, &new_dirp_l
);
240 resolve
.l_vmnt_lock
= VMNT_READ
;
241 resolve
.l_vnode_lock
= VNODE_WRITE
;
242 if (fetch_name(vname2
, vname2_length
, fullpath
) != OK
) r
= err_code
;
243 else if ((new_dirp
= last_dir(&resolve
, fp
)) == NULL
) r
= err_code
;
245 /* We used a separate vnode pointer to see whether we obtained a lock on the
246 * new_dirp vnode. If the new directory and old directory are the same, then
247 * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will
248 * be NULL, but new_dirp will not.
250 if (new_dirp
== old_dirp
) assert(new_dirp_l
== NULL
);
253 unlock_vnode(old_dirp
);
259 /* Both parent directories must be on the same device. */
260 if (old_dirp
->v_fs_e
!= new_dirp
->v_fs_e
) r
= EXDEV
;
262 /* Parent dirs must be writable, searchable and on a writable device */
263 if ((r1
= forbidden(fp
, old_dirp
, W_BIT
|X_BIT
)) != OK
||
264 (r1
= forbidden(fp
, new_dirp
, W_BIT
|X_BIT
)) != OK
) r
= r1
;
267 upgrade_vmnt_lock(oldvmp
); /* Upgrade to exclusive access */
268 r
= req_rename(old_dirp
->v_fs_e
, old_dirp
->v_inode_nr
, old_name
,
269 new_dirp
->v_inode_nr
, fullpath
);
272 unlock_vnode(old_dirp
);
274 if (new_dirp_l
) unlock_vnode(new_dirp_l
);
275 if (newvmp
) unlock_vmnt(newvmp
);
283 /*===========================================================================*
285 *===========================================================================*/
288 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
289 * do_truncate() and do_ftruncate() have to get hold of the inode, either
290 * by name or fd, do checks on it, and call truncate_inode() to do the
296 char fullpath
[PATH_MAX
];
297 struct lookup resolve
;
302 vname
= (vir_bytes
) job_m_in
.m2_p1
;
303 vname_length
= job_m_in
.m2_i1
;
305 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp
, &vp
);
306 resolve
.l_vmnt_lock
= VMNT_READ
;
307 resolve
.l_vnode_lock
= VNODE_WRITE
;
309 length
= (off_t
) job_m_in
.flength
;
310 if (length
< 0) return(EINVAL
);
312 /* Temporarily open file */
313 if (fetch_name(vname
, vname_length
, fullpath
) != OK
) return(err_code
);
314 if ((vp
= eat_path(&resolve
, fp
)) == NULL
) return(err_code
);
316 /* Ask FS to truncate the file */
317 if ((r
= forbidden(fp
, vp
, W_BIT
)) == OK
) {
318 /* If the file size does not change, do not make the actual call. This
319 * ensures that the file times are retained when the file size remains
320 * the same, which is a POSIX requirement.
322 if (S_ISREG(vp
->v_mode
) && vp
->v_size
== length
)
325 r
= truncate_vnode(vp
, length
);
334 /*===========================================================================*
336 *===========================================================================*/
339 /* As with do_truncate(), truncate_vnode() does the actual work. */
345 scratch(fp
).file
.fd_nr
= job_m_in
.fd
;
346 length
= (off_t
) job_m_in
.flength
;
348 if (length
< 0) return(EINVAL
);
350 /* File is already opened; get a vnode pointer from filp */
351 if ((rfilp
= get_filp(scratch(fp
).file
.fd_nr
, VNODE_WRITE
)) == NULL
)
354 vp
= rfilp
->filp_vno
;
356 if (!(rfilp
->filp_mode
& W_BIT
))
358 else if (S_ISREG(vp
->v_mode
) && vp
->v_size
== length
)
359 /* If the file size does not change, do not make the actual call. This
360 * ensures that the file times are retained when the file size remains
361 * the same, which is a POSIX requirement.
365 r
= truncate_vnode(vp
, length
);
372 /*===========================================================================*
374 *===========================================================================*/
375 int truncate_vnode(vp
, newsize
)
379 /* Truncate a regular file or a pipe */
382 assert(tll_locked_by_me(&vp
->v_lock
));
383 if (!S_ISREG(vp
->v_mode
) && !S_ISFIFO(vp
->v_mode
)) return(EINVAL
);
385 /* We must not compare the old and the new size here: this function may be
386 * called for open(2), which requires an update to the file times if O_TRUNC
387 * is given, even if the file size remains the same.
389 if ((r
= req_ftrunc(vp
->v_fs_e
, vp
->v_inode_nr
, newsize
, 0)) == OK
)
390 vp
->v_size
= newsize
;
395 /*===========================================================================*
397 *===========================================================================*/
400 /* Perform the symlink(name1, name2) system call. */
404 char fullpath
[PATH_MAX
];
405 struct lookup resolve
;
406 vir_bytes vname1
, vname2
;
407 size_t vname1_length
, vname2_length
;
409 lookup_init(&resolve
, fullpath
, PATH_NOFLAGS
, &vmp
, &vp
);
410 resolve
.l_vmnt_lock
= VMNT_WRITE
;
411 resolve
.l_vnode_lock
= VNODE_WRITE
;
413 vname1
= (vir_bytes
) job_m_in
.name1
;
414 vname1_length
= job_m_in
.name1_length
;
415 vname2
= (vir_bytes
) job_m_in
.name2
;
416 vname2_length
= job_m_in
.name2_length
;
418 if (vname1_length
<= 1) return(ENOENT
);
419 if (vname1_length
>= SYMLINK_MAX
) return(ENAMETOOLONG
);
421 /* Get dir inode of 'name2' */
422 if (fetch_name(vname2
, vname2_length
, fullpath
) != OK
) return(err_code
);
423 if ((vp
= last_dir(&resolve
, fp
)) == NULL
) return(err_code
);
424 if ((r
= forbidden(fp
, vp
, W_BIT
|X_BIT
)) == OK
) {
425 r
= req_slink(vp
->v_fs_e
, vp
->v_inode_nr
, fullpath
, who_e
,
426 vname1
, vname1_length
- 1, fp
->fp_effuid
,
437 /*===========================================================================*
439 *===========================================================================*/
440 int rdlink_direct(orig_path
, link_path
, rfp
)
442 char link_path
[PATH_MAX
]; /* should have length PATH_MAX */
445 /* Perform a readlink()-like call from within the VFS */
449 struct lookup resolve
;
451 lookup_init(&resolve
, link_path
, PATH_RET_SYMLINK
, &vmp
, &vp
);
452 resolve
.l_vmnt_lock
= VMNT_READ
;
453 resolve
.l_vnode_lock
= VNODE_READ
;
455 /* Temporarily open the file containing the symbolic link. Use link_path
456 * for temporary storage to keep orig_path untouched. */
457 strncpy(link_path
, orig_path
, PATH_MAX
); /* PATH_MAX includes '\0' */
458 link_path
[PATH_MAX
- 1] = '\0';
459 if ((vp
= eat_path(&resolve
, rfp
)) == NULL
) return(err_code
);
461 /* Make sure this is a symbolic link */
462 if (!S_ISLNK(vp
->v_mode
))
465 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, NONE
, (vir_bytes
) link_path
,
468 if (r
> 0) link_path
[r
] = '\0'; /* Terminate string when succesful */
477 /*===========================================================================*
479 *===========================================================================*/
482 /* Perform the readlink(name, buf, bufsize) system call. */
486 char fullpath
[PATH_MAX
];
487 struct lookup resolve
;
489 size_t vname_length
, buf_size
;
492 vname
= (vir_bytes
) job_m_in
.name1
;
493 vname_length
= job_m_in
.name1_length
;
494 buf
= (vir_bytes
) job_m_in
.name2
;
495 buf_size
= (size_t) job_m_in
.nbytes
;
496 if (buf_size
> SSIZE_MAX
) return(EINVAL
);
498 lookup_init(&resolve
, fullpath
, PATH_RET_SYMLINK
, &vmp
, &vp
);
499 resolve
.l_vmnt_lock
= VMNT_READ
;
500 resolve
.l_vnode_lock
= VNODE_READ
;
502 /* Temporarily open the file containing the symbolic link */
503 if (fetch_name(vname
, vname_length
, fullpath
) != OK
) return(err_code
);
504 if ((vp
= eat_path(&resolve
, fp
)) == NULL
) return(err_code
);
506 /* Make sure this is a symbolic link */
507 if (!S_ISLNK(vp
->v_mode
))
510 r
= req_rdlink(vp
->v_fs_e
, vp
->v_inode_nr
, who_e
, buf
, buf_size
, 0);