xref: /minix/minix/servers/vfs/link.c (revision fb9c64b2)
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.
4  *
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
12  */
13 
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 <sys/dirent.h>
21 #include <assert.h>
22 #include "file.h"
23 #include "path.h"
24 #include "vnode.h"
25 
26 /*===========================================================================*
27  *				do_link					     *
28  *===========================================================================*/
29 int do_link(void)
30 {
31 /* Perform the link(name1, name2) system call. */
32   int r = OK;
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;
39 
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;
44 
45   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp);
46   resolve.l_vmnt_lock = VMNT_WRITE;
47   resolve.l_vnode_lock = VNODE_READ;
48 
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);
52 
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)
58 	r = err_code;
59   else if ((dirp = last_dir(&resolve, fp)) == NULL)
60 	r = err_code;
61 
62   if (r != OK) {
63 	unlock_vnode(vp);
64 	unlock_vmnt(vmp1);
65 	put_vnode(vp);
66 	return(r);
67   }
68 
69   /* Check for links across devices. */
70   if (vp->v_fs_e != dirp->v_fs_e)
71 	r = EXDEV;
72   else
73 	r = forbidden(fp, dirp, W_BIT | X_BIT);
74 
75   if (r == OK)
76 	r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath,
77 		     vp->v_inode_nr);
78 
79   unlock_vnode(vp);
80   unlock_vnode(dirp);
81   if (vmp2 != NULL) unlock_vmnt(vmp2);
82   unlock_vmnt(vmp1);
83   put_vnode(vp);
84   put_vnode(dirp);
85   return(r);
86 }
87 
88 /*===========================================================================*
89  *				do_unlink				     *
90  *===========================================================================*/
91 int do_unlink(void)
92 {
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.
97  */
98   struct vnode *dirp, *dirp_l, *vp;
99   struct vmnt *vmp, *vmp2;
100   int r;
101   char fullpath[PATH_MAX];
102   struct lookup resolve, stickycheck;
103 
104   if (copy_path(fullpath, sizeof(fullpath)) != OK)
105 	return(err_code);
106 
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;
110 
111   /* Get the last directory in the path. */
112   if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
113 
114   /* Make sure that the object is a directory */
115   if (!S_ISDIR(dirp->v_mode)) {
116 	unlock_vnode(dirp);
117 	unlock_vmnt(vmp);
118 	put_vnode(dirp);
119 	return(ENOTDIR);
120   }
121 
122   /* The caller must have both search and execute permission */
123   if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
124 	unlock_vnode(dirp);
125 	unlock_vmnt(vmp);
126 	put_vnode(dirp);
127 	return(r);
128   }
129 
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);
139 	if (vp != NULL) {
140 		if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
141 			r = EPERM;
142 		unlock_vnode(vp);
143 		put_vnode(vp);
144 	} else
145 		r = err_code;
146 	if (r != OK) {
147 		unlock_vnode(dirp);
148 		unlock_vmnt(vmp);
149 		put_vnode(dirp);
150 		return(r);
151 	}
152   }
153 
154   upgrade_vmnt_lock(vmp);
155 
156   if (job_call_nr == VFS_UNLINK)
157 	  r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
158   else
159 	  r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
160   unlock_vnode(dirp);
161   unlock_vmnt(vmp);
162   put_vnode(dirp);
163   return(r);
164 }
165 
166 /*===========================================================================*
167  *				do_rename				     *
168  *===========================================================================*/
169 int do_rename(void)
170 {
171 /* Perform the rename(name1, name2) system call. */
172   int r = OK, r1;
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;
180 
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;
185 
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;
190 
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);
194 
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);
204 	if (vp != NULL) {
205 		if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
206 			r = EPERM;
207 		unlock_vnode(vp);
208 		put_vnode(vp);
209 	} else
210 		r = err_code;
211 	if (r != OK) {
212 		unlock_vnode(old_dirp);
213 		unlock_vmnt(oldvmp);
214 		put_vnode(old_dirp);
215 		return(r);
216 	}
217   }
218 
219   /* Save the last component of the old name */
220   if (strlen(fullpath) >= sizeof(old_name)) {
221 	unlock_vnode(old_dirp);
222 	unlock_vmnt(oldvmp);
223 	put_vnode(old_dirp);
224 	return(ENAMETOOLONG);
225   }
226   strlcpy(old_name, fullpath, PATH_MAX);
227 
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;
234 
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.
239    */
240   if (new_dirp == old_dirp) assert(new_dirp_l == NULL);
241 
242   if (r != OK) {
243 	unlock_vnode(old_dirp);
244 	unlock_vmnt(oldvmp);
245 	put_vnode(old_dirp);
246 	return(r);
247   }
248 
249   /* Both parent directories must be on the same device. */
250   if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
251 
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;
255 
256   if (r == OK) {
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);
260   }
261 
262   unlock_vnode(old_dirp);
263   unlock_vmnt(oldvmp);
264   if (new_dirp_l) unlock_vnode(new_dirp_l);
265   if (newvmp) unlock_vmnt(newvmp);
266 
267   put_vnode(old_dirp);
268   put_vnode(new_dirp);
269 
270   return(r);
271 }
272 
273 /*===========================================================================*
274  *				do_truncate				     *
275  *===========================================================================*/
276 int do_truncate(void)
277 {
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
281  * work.
282  */
283   struct vnode *vp;
284   struct vmnt *vmp;
285   int r;
286   char fullpath[PATH_MAX];
287   struct lookup resolve;
288   off_t length;
289   vir_bytes vname;
290   size_t vname_length;
291 
292   vname = job_m_in.m_lc_vfs_truncate.name;
293   vname_length = job_m_in.m_lc_vfs_truncate.len;
294 
295   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
296   resolve.l_vmnt_lock = VMNT_READ;
297   resolve.l_vnode_lock = VNODE_WRITE;
298 
299   length = job_m_in.m_lc_vfs_truncate.offset;
300   if (length < 0) return(EINVAL);
301 
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);
305 
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.
311 	 */
312 	if (S_ISREG(vp->v_mode) && vp->v_size == length)
313 		r = OK;
314 	else
315 		r = truncate_vnode(vp, length);
316   }
317 
318   unlock_vnode(vp);
319   unlock_vmnt(vmp);
320   put_vnode(vp);
321   return(r);
322 }
323 
324 /*===========================================================================*
325  *				do_ftruncate				     *
326  *===========================================================================*/
327 int do_ftruncate(void)
328 {
329 /* As with do_truncate(), truncate_vnode() does the actual work. */
330   struct filp *rfilp;
331   struct vnode *vp;
332   int r, fd;
333   off_t length;
334 
335   fd = job_m_in.m_lc_vfs_truncate.fd;
336 
337   length = job_m_in.m_lc_vfs_truncate.offset;
338   if (length < 0) return(EINVAL);
339 
340   /* File is already opened; get a vnode pointer from filp */
341   if ((rfilp = get_filp(fd, VNODE_WRITE)) == NULL)
342 	return(err_code);
343 
344   vp = rfilp->filp_vno;
345 
346   if (!(rfilp->filp_mode & W_BIT))
347 	r = EBADF;
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.
352 	 */
353 	r = OK;
354   else
355 	r = truncate_vnode(vp, length);
356 
357   unlock_filp(rfilp);
358   return(r);
359 }
360 
361 
362 /*===========================================================================*
363  *				truncate_vnode				     *
364  *===========================================================================*/
365 int
366 truncate_vnode(struct vnode *vp, off_t newsize)
367 {
368 /* Truncate a regular file or a pipe */
369   int r;
370 
371   assert(tll_locked_by_me(&vp->v_lock));
372   if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
373 
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.
377    */
378   if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
379 	vp->v_size = newsize;
380   return(r);
381 }
382 
383 
384 /*===========================================================================*
385  *                             do_slink					     *
386  *===========================================================================*/
387 int do_slink(void)
388 {
389 /* Perform the symlink(name1, name2) system call. */
390   int r;
391   struct vnode *vp;
392   struct vmnt *vmp;
393   char fullpath[PATH_MAX];
394   struct lookup resolve;
395   vir_bytes vname1, vname2;
396   size_t vname1_length, vname2_length;
397 
398   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
399   resolve.l_vmnt_lock = VMNT_WRITE;
400   resolve.l_vnode_lock = VNODE_WRITE;
401 
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;
406 
407   if (vname1_length <= 1) return(ENOENT);
408   if (vname1_length >= _POSIX_SYMLINK_MAX) return(ENAMETOOLONG);
409 
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,
416 		      fp->fp_effgid);
417   }
418 
419   unlock_vnode(vp);
420   unlock_vmnt(vmp);
421   put_vnode(vp);
422 
423   return(r);
424 }
425 
426 /*===========================================================================*
427  *                              rdlink_direct                                *
428  *===========================================================================*/
429 int
430 rdlink_direct(
431 	char *orig_path,
432 	char link_path[PATH_MAX], /* should have length PATH_MAX */
433 	struct fproc *rfp
434 )
435 {
436 /* Perform a readlink()-like call from within the VFS */
437   int r;
438   struct vnode *vp;
439   struct vmnt *vmp;
440   struct lookup resolve;
441 
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;
445 
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);
451 
452   /* Make sure this is a symbolic link */
453   if (!S_ISLNK(vp->v_mode))
454 	r = EINVAL;
455   else
456 	r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path,
457 		       PATH_MAX - 1, 1);
458 
459   if (r > 0) link_path[r] = '\0';	/* Terminate string when succesful */
460 
461   unlock_vnode(vp);
462   unlock_vmnt(vmp);
463   put_vnode(vp);
464 
465   return r;
466 }
467 
468 /*===========================================================================*
469  *                             do_rdlink				     *
470  *===========================================================================*/
471 int do_rdlink(void)
472 {
473 /* Perform the readlink(name, buf, bufsize) system call. */
474   int r;
475   struct vnode *vp;
476   struct vmnt *vmp;
477   char fullpath[PATH_MAX];
478   struct lookup resolve;
479   vir_bytes vname;
480   size_t vname_length, buf_size;
481   vir_bytes buf;
482 
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);
488 
489   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
490   resolve.l_vmnt_lock = VMNT_READ;
491   resolve.l_vnode_lock = VNODE_READ;
492 
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);
496 
497   /* Make sure this is a symbolic link */
498   if (!S_ISLNK(vp->v_mode))
499 	r = EINVAL;
500   else
501 	r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0);
502 
503   unlock_vnode(vp);
504   unlock_vmnt(vmp);
505   put_vnode(vp);
506 
507   return(r);
508 }
509