xref: /minix3/minix/servers/vfs/link.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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 #include "scratchpad.h"
26 
27 /*===========================================================================*
28  *				do_link					     *
29  *===========================================================================*/
30 int do_link(void)
31 {
32 /* Perform the link(name1, name2) system call. */
33   int r = OK;
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;
40 
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;
45 
46   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp);
47   resolve.l_vmnt_lock = VMNT_WRITE;
48   resolve.l_vnode_lock = VNODE_READ;
49 
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);
53 
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)
59 	r = err_code;
60   else if ((dirp = last_dir(&resolve, fp)) == NULL)
61 	r = err_code;
62 
63   if (r != OK) {
64 	unlock_vnode(vp);
65 	unlock_vmnt(vmp1);
66 	put_vnode(vp);
67 	return(r);
68   }
69 
70   /* Check for links across devices. */
71   if (vp->v_fs_e != dirp->v_fs_e)
72 	r = EXDEV;
73   else
74 	r = forbidden(fp, dirp, W_BIT | X_BIT);
75 
76   if (r == OK)
77 	r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath,
78 		     vp->v_inode_nr);
79 
80   unlock_vnode(vp);
81   unlock_vnode(dirp);
82   if (vmp2 != NULL) unlock_vmnt(vmp2);
83   unlock_vmnt(vmp1);
84   put_vnode(vp);
85   put_vnode(dirp);
86   return(r);
87 }
88 
89 /*===========================================================================*
90  *				do_unlink				     *
91  *===========================================================================*/
92 int do_unlink(void)
93 {
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.
98  */
99   struct vnode *dirp, *dirp_l, *vp;
100   struct vmnt *vmp, *vmp2;
101   int r;
102   char fullpath[PATH_MAX];
103   struct lookup resolve, stickycheck;
104 
105   if (copy_path(fullpath, sizeof(fullpath)) != OK)
106 	return(err_code);
107 
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;
111 
112   /* Get the last directory in the path. */
113   if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
114 
115   /* Make sure that the object is a directory */
116   if (!S_ISDIR(dirp->v_mode)) {
117 	unlock_vnode(dirp);
118 	unlock_vmnt(vmp);
119 	put_vnode(dirp);
120 	return(ENOTDIR);
121   }
122 
123   /* The caller must have both search and execute permission */
124   if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
125 	unlock_vnode(dirp);
126 	unlock_vmnt(vmp);
127 	put_vnode(dirp);
128 	return(r);
129   }
130 
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);
140 	if (vp != NULL) {
141 		if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
142 			r = EPERM;
143 		unlock_vnode(vp);
144 		put_vnode(vp);
145 	} else
146 		r = err_code;
147 	if (r != OK) {
148 		unlock_vnode(dirp);
149 		unlock_vmnt(vmp);
150 		put_vnode(dirp);
151 		return(r);
152 	}
153   }
154 
155   upgrade_vmnt_lock(vmp);
156 
157   if (job_call_nr == VFS_UNLINK)
158 	  r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
159   else
160 	  r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
161   unlock_vnode(dirp);
162   unlock_vmnt(vmp);
163   put_vnode(dirp);
164   return(r);
165 }
166 
167 /*===========================================================================*
168  *				do_rename				     *
169  *===========================================================================*/
170 int do_rename(void)
171 {
172 /* Perform the rename(name1, name2) system call. */
173   int r = OK, r1;
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;
181 
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;
186 
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;
191 
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);
195 
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);
205 	if (vp != NULL) {
206 		if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
207 			r = EPERM;
208 		unlock_vnode(vp);
209 		put_vnode(vp);
210 	} else
211 		r = err_code;
212 	if (r != OK) {
213 		unlock_vnode(old_dirp);
214 		unlock_vmnt(oldvmp);
215 		put_vnode(old_dirp);
216 		return(r);
217 	}
218   }
219 
220   /* Save the last component of the old name */
221   if (strlen(fullpath) >= sizeof(old_name)) {
222 	unlock_vnode(old_dirp);
223 	unlock_vmnt(oldvmp);
224 	put_vnode(old_dirp);
225 	return(ENAMETOOLONG);
226   }
227   strlcpy(old_name, fullpath, PATH_MAX);
228 
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;
235 
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.
240    */
241   if (new_dirp == old_dirp) assert(new_dirp_l == NULL);
242 
243   if (r != OK) {
244 	unlock_vnode(old_dirp);
245 	unlock_vmnt(oldvmp);
246 	put_vnode(old_dirp);
247 	return(r);
248   }
249 
250   /* Both parent directories must be on the same device. */
251   if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
252 
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;
256 
257   if (r == OK) {
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);
261   }
262 
263   unlock_vnode(old_dirp);
264   unlock_vmnt(oldvmp);
265   if (new_dirp_l) unlock_vnode(new_dirp_l);
266   if (newvmp) unlock_vmnt(newvmp);
267 
268   put_vnode(old_dirp);
269   put_vnode(new_dirp);
270 
271   return(r);
272 }
273 
274 /*===========================================================================*
275  *				do_truncate				     *
276  *===========================================================================*/
277 int do_truncate(void)
278 {
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
282  * work.
283  */
284   struct vnode *vp;
285   struct vmnt *vmp;
286   int r;
287   char fullpath[PATH_MAX];
288   struct lookup resolve;
289   off_t length;
290   vir_bytes vname;
291   size_t vname_length;
292 
293   vname = job_m_in.m_lc_vfs_truncate.name;
294   vname_length = job_m_in.m_lc_vfs_truncate.len;
295 
296   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
297   resolve.l_vmnt_lock = VMNT_READ;
298   resolve.l_vnode_lock = VNODE_WRITE;
299 
300   length = job_m_in.m_lc_vfs_truncate.offset;
301   if (length < 0) return(EINVAL);
302 
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);
306 
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.
312 	 */
313 	if (S_ISREG(vp->v_mode) && vp->v_size == length)
314 		r = OK;
315 	else
316 		r = truncate_vnode(vp, length);
317   }
318 
319   unlock_vnode(vp);
320   unlock_vmnt(vmp);
321   put_vnode(vp);
322   return(r);
323 }
324 
325 /*===========================================================================*
326  *				do_ftruncate				     *
327  *===========================================================================*/
328 int do_ftruncate(void)
329 {
330 /* As with do_truncate(), truncate_vnode() does the actual work. */
331   struct filp *rfilp;
332   struct vnode *vp;
333   int r;
334   off_t length;
335 
336   scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_truncate.fd;
337 
338   length = job_m_in.m_lc_vfs_truncate.offset;
339   if (length < 0) return(EINVAL);
340 
341   /* File is already opened; get a vnode pointer from filp */
342   if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL)
343 	return(err_code);
344 
345   vp = rfilp->filp_vno;
346 
347   if (!(rfilp->filp_mode & W_BIT))
348 	r = EBADF;
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.
353 	 */
354 	r = OK;
355   else
356 	r = truncate_vnode(vp, length);
357 
358   unlock_filp(rfilp);
359   return(r);
360 }
361 
362 
363 /*===========================================================================*
364  *				truncate_vnode				     *
365  *===========================================================================*/
366 int truncate_vnode(vp, newsize)
367 struct vnode *vp;
368 off_t newsize;
369 {
370 /* Truncate a regular file or a pipe */
371   int r;
372 
373   assert(tll_locked_by_me(&vp->v_lock));
374   if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
375 
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.
379    */
380   if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
381 	vp->v_size = newsize;
382   return(r);
383 }
384 
385 
386 /*===========================================================================*
387  *                             do_slink					     *
388  *===========================================================================*/
389 int do_slink(void)
390 {
391 /* Perform the symlink(name1, name2) system call. */
392   int r;
393   struct vnode *vp;
394   struct vmnt *vmp;
395   char fullpath[PATH_MAX];
396   struct lookup resolve;
397   vir_bytes vname1, vname2;
398   size_t vname1_length, vname2_length;
399 
400   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
401   resolve.l_vmnt_lock = VMNT_WRITE;
402   resolve.l_vnode_lock = VNODE_WRITE;
403 
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;
408 
409   if (vname1_length <= 1) return(ENOENT);
410   if (vname1_length >= _POSIX_SYMLINK_MAX) return(ENAMETOOLONG);
411 
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,
418 		      fp->fp_effgid);
419   }
420 
421   unlock_vnode(vp);
422   unlock_vmnt(vmp);
423   put_vnode(vp);
424 
425   return(r);
426 }
427 
428 /*===========================================================================*
429  *                              rdlink_direct                                *
430  *===========================================================================*/
431 int rdlink_direct(orig_path, link_path, rfp)
432 char *orig_path;
433 char link_path[PATH_MAX]; /* should have length PATH_MAX */
434 struct fproc *rfp;
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