xref: /minix3/minix/servers/vfs/link.c (revision a0814afb2e128f52ed10c9a5d4c91bf6abc22290)
1433d6423SLionel Sambuc /* This file handles the LINK and UNLINK system calls.  It also deals with
2433d6423SLionel Sambuc  * deallocating the storage used by a file when the last UNLINK is done to a
3433d6423SLionel Sambuc  * file and the blocks must be returned to the free block pool.
4433d6423SLionel Sambuc  *
5433d6423SLionel Sambuc  * The entry points into this file are
6433d6423SLionel Sambuc  *   do_link:         perform the LINK system call
7433d6423SLionel Sambuc  *   do_unlink:	      perform the UNLINK and RMDIR system calls
8433d6423SLionel Sambuc  *   do_rename:	      perform the RENAME system call
9433d6423SLionel Sambuc  *   do_truncate:     perform the TRUNCATE system call
10433d6423SLionel Sambuc  *   do_ftruncate:    perform the FTRUNCATE system call
11433d6423SLionel Sambuc  *   do_rdlink:       perform the RDLNK system call
12433d6423SLionel Sambuc  */
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc #include "fs.h"
15433d6423SLionel Sambuc #include <sys/stat.h>
16433d6423SLionel Sambuc #include <string.h>
17433d6423SLionel Sambuc #include <minix/com.h>
18433d6423SLionel Sambuc #include <minix/callnr.h>
19433d6423SLionel Sambuc #include <minix/vfsif.h>
20433d6423SLionel Sambuc #include <sys/dirent.h>
21433d6423SLionel Sambuc #include <assert.h>
22433d6423SLionel Sambuc #include "file.h"
23433d6423SLionel Sambuc #include "path.h"
24433d6423SLionel Sambuc #include "vnode.h"
25433d6423SLionel Sambuc 
26433d6423SLionel Sambuc /*===========================================================================*
27433d6423SLionel Sambuc  *				do_link					     *
28433d6423SLionel Sambuc  *===========================================================================*/
do_link(void)29433d6423SLionel Sambuc int do_link(void)
30433d6423SLionel Sambuc {
31433d6423SLionel Sambuc /* Perform the link(name1, name2) system call. */
32433d6423SLionel Sambuc   int r = OK;
33433d6423SLionel Sambuc   struct vnode *vp = NULL, *dirp = NULL;
34433d6423SLionel Sambuc   struct vmnt *vmp1 = NULL, *vmp2 = NULL;
35433d6423SLionel Sambuc   char fullpath[PATH_MAX];
36433d6423SLionel Sambuc   struct lookup resolve;
37433d6423SLionel Sambuc   vir_bytes vname1, vname2;
38433d6423SLionel Sambuc   size_t vname1_length, vname2_length;
39433d6423SLionel Sambuc 
40433d6423SLionel Sambuc   vname1 = job_m_in.m_lc_vfs_link.name1;
41433d6423SLionel Sambuc   vname1_length = job_m_in.m_lc_vfs_link.len1;
42433d6423SLionel Sambuc   vname2 = job_m_in.m_lc_vfs_link.name2;
43433d6423SLionel Sambuc   vname2_length = job_m_in.m_lc_vfs_link.len2;
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp);
46433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_WRITE;
47433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_READ;
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc   /* See if 'name1' (file to be linked to) exists. */
50433d6423SLionel Sambuc   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
51433d6423SLionel Sambuc   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc   /* Does the final directory of 'name2' exist? */
54433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp2, &dirp);
55433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
56433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
57433d6423SLionel Sambuc   if (fetch_name(vname2, vname2_length, fullpath) != OK)
58433d6423SLionel Sambuc 	r = err_code;
59433d6423SLionel Sambuc   else if ((dirp = last_dir(&resolve, fp)) == NULL)
60433d6423SLionel Sambuc 	r = err_code;
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc   if (r != OK) {
63433d6423SLionel Sambuc 	unlock_vnode(vp);
64433d6423SLionel Sambuc 	unlock_vmnt(vmp1);
65433d6423SLionel Sambuc 	put_vnode(vp);
66433d6423SLionel Sambuc 	return(r);
67433d6423SLionel Sambuc   }
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc   /* Check for links across devices. */
70433d6423SLionel Sambuc   if (vp->v_fs_e != dirp->v_fs_e)
71433d6423SLionel Sambuc 	r = EXDEV;
72433d6423SLionel Sambuc   else
73433d6423SLionel Sambuc 	r = forbidden(fp, dirp, W_BIT | X_BIT);
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc   if (r == OK)
76433d6423SLionel Sambuc 	r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath,
77433d6423SLionel Sambuc 		     vp->v_inode_nr);
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc   unlock_vnode(vp);
80433d6423SLionel Sambuc   unlock_vnode(dirp);
81433d6423SLionel Sambuc   if (vmp2 != NULL) unlock_vmnt(vmp2);
82433d6423SLionel Sambuc   unlock_vmnt(vmp1);
83433d6423SLionel Sambuc   put_vnode(vp);
84433d6423SLionel Sambuc   put_vnode(dirp);
85433d6423SLionel Sambuc   return(r);
86433d6423SLionel Sambuc }
87433d6423SLionel Sambuc 
88433d6423SLionel Sambuc /*===========================================================================*
89433d6423SLionel Sambuc  *				do_unlink				     *
90433d6423SLionel Sambuc  *===========================================================================*/
do_unlink(void)91433d6423SLionel Sambuc int do_unlink(void)
92433d6423SLionel Sambuc {
93433d6423SLionel Sambuc /* Perform the unlink(name) or rmdir(name) system call. The code for these two
94433d6423SLionel Sambuc  * is almost the same.  They differ only in some condition testing.  Unlink()
95433d6423SLionel Sambuc  * may be used by the superuser to do dangerous things; rmdir() may not.
96433d6423SLionel Sambuc  * The syscall might provide 'name' embedded in the message.
97433d6423SLionel Sambuc  */
98433d6423SLionel Sambuc   struct vnode *dirp, *dirp_l, *vp;
99433d6423SLionel Sambuc   struct vmnt *vmp, *vmp2;
100433d6423SLionel Sambuc   int r;
101433d6423SLionel Sambuc   char fullpath[PATH_MAX];
102433d6423SLionel Sambuc   struct lookup resolve, stickycheck;
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc   if (copy_path(fullpath, sizeof(fullpath)) != OK)
105433d6423SLionel Sambuc 	return(err_code);
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &dirp_l);
108433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_WRITE;
109433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
110433d6423SLionel Sambuc 
111433d6423SLionel Sambuc   /* Get the last directory in the path. */
112433d6423SLionel Sambuc   if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc   /* Make sure that the object is a directory */
115433d6423SLionel Sambuc   if (!S_ISDIR(dirp->v_mode)) {
116433d6423SLionel Sambuc 	unlock_vnode(dirp);
117433d6423SLionel Sambuc 	unlock_vmnt(vmp);
118433d6423SLionel Sambuc 	put_vnode(dirp);
119433d6423SLionel Sambuc 	return(ENOTDIR);
120433d6423SLionel Sambuc   }
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc   /* The caller must have both search and execute permission */
123433d6423SLionel Sambuc   if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) {
124433d6423SLionel Sambuc 	unlock_vnode(dirp);
125433d6423SLionel Sambuc 	unlock_vmnt(vmp);
126433d6423SLionel Sambuc 	put_vnode(dirp);
127433d6423SLionel Sambuc 	return(r);
128433d6423SLionel Sambuc   }
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc   /* Also, if the sticky bit is set, only the owner of the file or a privileged
131433d6423SLionel Sambuc      user is allowed to unlink */
132433d6423SLionel Sambuc   if ((dirp->v_mode & S_ISVTX) == S_ISVTX) {
133433d6423SLionel Sambuc 	/* Look up inode of file to unlink to retrieve owner */
134433d6423SLionel Sambuc 	lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
135433d6423SLionel Sambuc 	stickycheck.l_vmnt_lock = VMNT_READ;
136433d6423SLionel Sambuc 	stickycheck.l_vnode_lock = VNODE_READ;
137433d6423SLionel Sambuc 	vp = advance(dirp, &stickycheck, fp);
138433d6423SLionel Sambuc 	assert(vmp2 == NULL);
139433d6423SLionel Sambuc 	if (vp != NULL) {
140433d6423SLionel Sambuc 		if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
141433d6423SLionel Sambuc 			r = EPERM;
142433d6423SLionel Sambuc 		unlock_vnode(vp);
143433d6423SLionel Sambuc 		put_vnode(vp);
144433d6423SLionel Sambuc 	} else
145433d6423SLionel Sambuc 		r = err_code;
146433d6423SLionel Sambuc 	if (r != OK) {
147433d6423SLionel Sambuc 		unlock_vnode(dirp);
148433d6423SLionel Sambuc 		unlock_vmnt(vmp);
149433d6423SLionel Sambuc 		put_vnode(dirp);
150433d6423SLionel Sambuc 		return(r);
151433d6423SLionel Sambuc 	}
152433d6423SLionel Sambuc   }
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc   upgrade_vmnt_lock(vmp);
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc   if (job_call_nr == VFS_UNLINK)
157433d6423SLionel Sambuc 	  r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
158433d6423SLionel Sambuc   else
159433d6423SLionel Sambuc 	  r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath);
160433d6423SLionel Sambuc   unlock_vnode(dirp);
161433d6423SLionel Sambuc   unlock_vmnt(vmp);
162433d6423SLionel Sambuc   put_vnode(dirp);
163433d6423SLionel Sambuc   return(r);
164433d6423SLionel Sambuc }
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc /*===========================================================================*
167433d6423SLionel Sambuc  *				do_rename				     *
168433d6423SLionel Sambuc  *===========================================================================*/
do_rename(void)169433d6423SLionel Sambuc int do_rename(void)
170433d6423SLionel Sambuc {
171433d6423SLionel Sambuc /* Perform the rename(name1, name2) system call. */
172433d6423SLionel Sambuc   int r = OK, r1;
173433d6423SLionel Sambuc   struct vnode *old_dirp = NULL, *new_dirp = NULL, *new_dirp_l = NULL, *vp;
174433d6423SLionel Sambuc   struct vmnt *oldvmp, *newvmp, *vmp2;
175433d6423SLionel Sambuc   char old_name[PATH_MAX];
176433d6423SLionel Sambuc   char fullpath[PATH_MAX];
177433d6423SLionel Sambuc   struct lookup resolve, stickycheck;
178433d6423SLionel Sambuc   vir_bytes vname1, vname2;
179433d6423SLionel Sambuc   size_t vname1_length, vname2_length;
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc   vname1 = job_m_in.m_lc_vfs_link.name1;
182433d6423SLionel Sambuc   vname1_length = job_m_in.m_lc_vfs_link.len1;
183433d6423SLionel Sambuc   vname2 = job_m_in.m_lc_vfs_link.name2;
184433d6423SLionel Sambuc   vname2_length = job_m_in.m_lc_vfs_link.len2;
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &oldvmp, &old_dirp);
187433d6423SLionel Sambuc   /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */
188433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_WRITE;
189433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
190433d6423SLionel Sambuc 
191433d6423SLionel Sambuc   /* See if 'name1' (existing file) exists.  Get dir and file inodes. */
192433d6423SLionel Sambuc   if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
193433d6423SLionel Sambuc   if ((old_dirp = last_dir(&resolve, fp)) == NULL) return(err_code);
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc   /* If the sticky bit is set, only the owner of the file or a privileged
196433d6423SLionel Sambuc      user is allowed to rename */
197433d6423SLionel Sambuc   if ((old_dirp->v_mode & S_ISVTX) == S_ISVTX) {
198433d6423SLionel Sambuc 	/* Look up inode of file to unlink to retrieve owner */
199433d6423SLionel Sambuc 	lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp);
200433d6423SLionel Sambuc 	stickycheck.l_vmnt_lock = VMNT_READ;
201433d6423SLionel Sambuc 	stickycheck.l_vnode_lock = VNODE_READ;
202433d6423SLionel Sambuc 	vp = advance(old_dirp, &stickycheck, fp);
203433d6423SLionel Sambuc 	assert(vmp2 == NULL);
204433d6423SLionel Sambuc 	if (vp != NULL) {
205433d6423SLionel Sambuc 		if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID)
206433d6423SLionel Sambuc 			r = EPERM;
207433d6423SLionel Sambuc 		unlock_vnode(vp);
208433d6423SLionel Sambuc 		put_vnode(vp);
209433d6423SLionel Sambuc 	} else
210433d6423SLionel Sambuc 		r = err_code;
211433d6423SLionel Sambuc 	if (r != OK) {
212433d6423SLionel Sambuc 		unlock_vnode(old_dirp);
213433d6423SLionel Sambuc 		unlock_vmnt(oldvmp);
214433d6423SLionel Sambuc 		put_vnode(old_dirp);
215433d6423SLionel Sambuc 		return(r);
216433d6423SLionel Sambuc 	}
217433d6423SLionel Sambuc   }
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc   /* Save the last component of the old name */
220433d6423SLionel Sambuc   if (strlen(fullpath) >= sizeof(old_name)) {
221433d6423SLionel Sambuc 	unlock_vnode(old_dirp);
222433d6423SLionel Sambuc 	unlock_vmnt(oldvmp);
223433d6423SLionel Sambuc 	put_vnode(old_dirp);
224433d6423SLionel Sambuc 	return(ENAMETOOLONG);
225433d6423SLionel Sambuc   }
226433d6423SLionel Sambuc   strlcpy(old_name, fullpath, PATH_MAX);
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc   /* See if 'name2' (new name) exists.  Get dir inode */
229433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &newvmp, &new_dirp_l);
230433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
231433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
232433d6423SLionel Sambuc   if (fetch_name(vname2, vname2_length, fullpath) != OK) r = err_code;
233433d6423SLionel Sambuc   else if ((new_dirp = last_dir(&resolve, fp)) == NULL) r = err_code;
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc   /* We used a separate vnode pointer to see whether we obtained a lock on the
236433d6423SLionel Sambuc    * new_dirp vnode. If the new directory and old directory are the same, then
237433d6423SLionel Sambuc    * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will
238433d6423SLionel Sambuc    * be NULL, but new_dirp will not.
239433d6423SLionel Sambuc    */
240433d6423SLionel Sambuc   if (new_dirp == old_dirp) assert(new_dirp_l == NULL);
241433d6423SLionel Sambuc 
242433d6423SLionel Sambuc   if (r != OK) {
243433d6423SLionel Sambuc 	unlock_vnode(old_dirp);
244433d6423SLionel Sambuc 	unlock_vmnt(oldvmp);
245433d6423SLionel Sambuc 	put_vnode(old_dirp);
246433d6423SLionel Sambuc 	return(r);
247433d6423SLionel Sambuc   }
248433d6423SLionel Sambuc 
249433d6423SLionel Sambuc   /* Both parent directories must be on the same device. */
250433d6423SLionel Sambuc   if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV;
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc   /* Parent dirs must be writable, searchable and on a writable device */
253433d6423SLionel Sambuc   if ((r1 = forbidden(fp, old_dirp, W_BIT|X_BIT)) != OK ||
254433d6423SLionel Sambuc       (r1 = forbidden(fp, new_dirp, W_BIT|X_BIT)) != OK) r = r1;
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc   if (r == OK) {
257433d6423SLionel Sambuc 	upgrade_vmnt_lock(oldvmp); /* Upgrade to exclusive access */
258433d6423SLionel Sambuc 	r = req_rename(old_dirp->v_fs_e, old_dirp->v_inode_nr, old_name,
259433d6423SLionel Sambuc 		       new_dirp->v_inode_nr, fullpath);
260433d6423SLionel Sambuc   }
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc   unlock_vnode(old_dirp);
263433d6423SLionel Sambuc   unlock_vmnt(oldvmp);
264433d6423SLionel Sambuc   if (new_dirp_l) unlock_vnode(new_dirp_l);
265433d6423SLionel Sambuc   if (newvmp) unlock_vmnt(newvmp);
266433d6423SLionel Sambuc 
267433d6423SLionel Sambuc   put_vnode(old_dirp);
268433d6423SLionel Sambuc   put_vnode(new_dirp);
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc   return(r);
271433d6423SLionel Sambuc }
272433d6423SLionel Sambuc 
273433d6423SLionel Sambuc /*===========================================================================*
274433d6423SLionel Sambuc  *				do_truncate				     *
275433d6423SLionel Sambuc  *===========================================================================*/
do_truncate(void)276433d6423SLionel Sambuc int do_truncate(void)
277433d6423SLionel Sambuc {
278433d6423SLionel Sambuc /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate().
279433d6423SLionel Sambuc  * do_truncate() and do_ftruncate() have to get hold of the inode, either
280433d6423SLionel Sambuc  * by name or fd, do checks on it, and call truncate_inode() to do the
281433d6423SLionel Sambuc  * work.
282433d6423SLionel Sambuc  */
283433d6423SLionel Sambuc   struct vnode *vp;
284433d6423SLionel Sambuc   struct vmnt *vmp;
285433d6423SLionel Sambuc   int r;
286433d6423SLionel Sambuc   char fullpath[PATH_MAX];
287433d6423SLionel Sambuc   struct lookup resolve;
288433d6423SLionel Sambuc   off_t length;
289433d6423SLionel Sambuc   vir_bytes vname;
290433d6423SLionel Sambuc   size_t vname_length;
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc   vname = job_m_in.m_lc_vfs_truncate.name;
293433d6423SLionel Sambuc   vname_length = job_m_in.m_lc_vfs_truncate.len;
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
296433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
297433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc   length = job_m_in.m_lc_vfs_truncate.offset;
300433d6423SLionel Sambuc   if (length < 0) return(EINVAL);
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc   /* Temporarily open file */
303433d6423SLionel Sambuc   if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
304433d6423SLionel Sambuc   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
305433d6423SLionel Sambuc 
306433d6423SLionel Sambuc   /* Ask FS to truncate the file */
307433d6423SLionel Sambuc   if ((r = forbidden(fp, vp, W_BIT)) == OK) {
308433d6423SLionel Sambuc 	/* If the file size does not change, do not make the actual call. This
309433d6423SLionel Sambuc 	 * ensures that the file times are retained when the file size remains
310433d6423SLionel Sambuc 	 * the same, which is a POSIX requirement.
311433d6423SLionel Sambuc 	 */
312433d6423SLionel Sambuc 	if (S_ISREG(vp->v_mode) && vp->v_size == length)
313433d6423SLionel Sambuc 		r = OK;
314433d6423SLionel Sambuc 	else
315433d6423SLionel Sambuc 		r = truncate_vnode(vp, length);
316433d6423SLionel Sambuc   }
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc   unlock_vnode(vp);
319433d6423SLionel Sambuc   unlock_vmnt(vmp);
320433d6423SLionel Sambuc   put_vnode(vp);
321433d6423SLionel Sambuc   return(r);
322433d6423SLionel Sambuc }
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc /*===========================================================================*
325433d6423SLionel Sambuc  *				do_ftruncate				     *
326433d6423SLionel Sambuc  *===========================================================================*/
do_ftruncate(void)327433d6423SLionel Sambuc int do_ftruncate(void)
328433d6423SLionel Sambuc {
329433d6423SLionel Sambuc /* As with do_truncate(), truncate_vnode() does the actual work. */
330433d6423SLionel Sambuc   struct filp *rfilp;
331433d6423SLionel Sambuc   struct vnode *vp;
332232819ddSDavid van Moolenbroek   int r, fd;
333433d6423SLionel Sambuc   off_t length;
334433d6423SLionel Sambuc 
335232819ddSDavid van Moolenbroek   fd = job_m_in.m_lc_vfs_truncate.fd;
336433d6423SLionel Sambuc 
337433d6423SLionel Sambuc   length = job_m_in.m_lc_vfs_truncate.offset;
338433d6423SLionel Sambuc   if (length < 0) return(EINVAL);
339433d6423SLionel Sambuc 
340433d6423SLionel Sambuc   /* File is already opened; get a vnode pointer from filp */
341232819ddSDavid van Moolenbroek   if ((rfilp = get_filp(fd, VNODE_WRITE)) == NULL)
342433d6423SLionel Sambuc 	return(err_code);
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc   vp = rfilp->filp_vno;
345433d6423SLionel Sambuc 
346433d6423SLionel Sambuc   if (!(rfilp->filp_mode & W_BIT))
347433d6423SLionel Sambuc 	r = EBADF;
348433d6423SLionel Sambuc   else if (S_ISREG(vp->v_mode) && vp->v_size == length)
349433d6423SLionel Sambuc 	/* If the file size does not change, do not make the actual call. This
350433d6423SLionel Sambuc 	 * ensures that the file times are retained when the file size remains
351433d6423SLionel Sambuc 	 * the same, which is a POSIX requirement.
352433d6423SLionel Sambuc 	 */
353433d6423SLionel Sambuc 	r = OK;
354433d6423SLionel Sambuc   else
355433d6423SLionel Sambuc 	r = truncate_vnode(vp, length);
356433d6423SLionel Sambuc 
357433d6423SLionel Sambuc   unlock_filp(rfilp);
358433d6423SLionel Sambuc   return(r);
359433d6423SLionel Sambuc }
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc /*===========================================================================*
363433d6423SLionel Sambuc  *				truncate_vnode				     *
364433d6423SLionel Sambuc  *===========================================================================*/
365*a0814afbSRichard Sailer int
truncate_vnode(struct vnode * vp,off_t newsize)366*a0814afbSRichard Sailer truncate_vnode(struct vnode *vp, off_t newsize)
367433d6423SLionel Sambuc {
368433d6423SLionel Sambuc /* Truncate a regular file or a pipe */
369433d6423SLionel Sambuc   int r;
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc   assert(tll_locked_by_me(&vp->v_lock));
372433d6423SLionel Sambuc   if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL);
373433d6423SLionel Sambuc 
374433d6423SLionel Sambuc   /* We must not compare the old and the new size here: this function may be
375433d6423SLionel Sambuc    * called for open(2), which requires an update to the file times if O_TRUNC
376433d6423SLionel Sambuc    * is given, even if the file size remains the same.
377433d6423SLionel Sambuc    */
378433d6423SLionel Sambuc   if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK)
379433d6423SLionel Sambuc 	vp->v_size = newsize;
380433d6423SLionel Sambuc   return(r);
381433d6423SLionel Sambuc }
382433d6423SLionel Sambuc 
383433d6423SLionel Sambuc 
384433d6423SLionel Sambuc /*===========================================================================*
385433d6423SLionel Sambuc  *                             do_slink					     *
386433d6423SLionel Sambuc  *===========================================================================*/
do_slink(void)387433d6423SLionel Sambuc int do_slink(void)
388433d6423SLionel Sambuc {
389433d6423SLionel Sambuc /* Perform the symlink(name1, name2) system call. */
390433d6423SLionel Sambuc   int r;
391433d6423SLionel Sambuc   struct vnode *vp;
392433d6423SLionel Sambuc   struct vmnt *vmp;
393433d6423SLionel Sambuc   char fullpath[PATH_MAX];
394433d6423SLionel Sambuc   struct lookup resolve;
395433d6423SLionel Sambuc   vir_bytes vname1, vname2;
396433d6423SLionel Sambuc   size_t vname1_length, vname2_length;
397433d6423SLionel Sambuc 
398433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp);
399433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_WRITE;
400433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_WRITE;
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc   vname1 = job_m_in.m_lc_vfs_link.name1;
403433d6423SLionel Sambuc   vname1_length = job_m_in.m_lc_vfs_link.len1;
404433d6423SLionel Sambuc   vname2 = job_m_in.m_lc_vfs_link.name2;
405433d6423SLionel Sambuc   vname2_length = job_m_in.m_lc_vfs_link.len2;
406433d6423SLionel Sambuc 
407433d6423SLionel Sambuc   if (vname1_length <= 1) return(ENOENT);
408433d6423SLionel Sambuc   if (vname1_length >= _POSIX_SYMLINK_MAX) return(ENAMETOOLONG);
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc   /* Get dir inode of 'name2' */
411433d6423SLionel Sambuc   if (fetch_name(vname2, vname2_length, fullpath) != OK) return(err_code);
412433d6423SLionel Sambuc   if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code);
413433d6423SLionel Sambuc   if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) {
414433d6423SLionel Sambuc 	r = req_slink(vp->v_fs_e, vp->v_inode_nr, fullpath, who_e,
415433d6423SLionel Sambuc 		      vname1, vname1_length - 1, fp->fp_effuid,
416433d6423SLionel Sambuc 		      fp->fp_effgid);
417433d6423SLionel Sambuc   }
418433d6423SLionel Sambuc 
419433d6423SLionel Sambuc   unlock_vnode(vp);
420433d6423SLionel Sambuc   unlock_vmnt(vmp);
421433d6423SLionel Sambuc   put_vnode(vp);
422433d6423SLionel Sambuc 
423433d6423SLionel Sambuc   return(r);
424433d6423SLionel Sambuc }
425433d6423SLionel Sambuc 
426433d6423SLionel Sambuc /*===========================================================================*
427433d6423SLionel Sambuc  *                              rdlink_direct                                *
428433d6423SLionel Sambuc  *===========================================================================*/
429*a0814afbSRichard Sailer int
rdlink_direct(char * orig_path,char link_path[PATH_MAX],struct fproc * rfp)430*a0814afbSRichard Sailer rdlink_direct(
431*a0814afbSRichard Sailer 	char *orig_path,
432*a0814afbSRichard Sailer 	char link_path[PATH_MAX], /* should have length PATH_MAX */
433*a0814afbSRichard Sailer 	struct fproc *rfp
434*a0814afbSRichard Sailer )
435433d6423SLionel Sambuc {
436433d6423SLionel Sambuc /* Perform a readlink()-like call from within the VFS */
437433d6423SLionel Sambuc   int r;
438433d6423SLionel Sambuc   struct vnode *vp;
439433d6423SLionel Sambuc   struct vmnt *vmp;
440433d6423SLionel Sambuc   struct lookup resolve;
441433d6423SLionel Sambuc 
442433d6423SLionel Sambuc   lookup_init(&resolve, link_path, PATH_RET_SYMLINK, &vmp, &vp);
443433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
444433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_READ;
445433d6423SLionel Sambuc 
446433d6423SLionel Sambuc   /* Temporarily open the file containing the symbolic link. Use link_path
447433d6423SLionel Sambuc    * for temporary storage to keep orig_path untouched. */
448433d6423SLionel Sambuc   strncpy(link_path, orig_path, PATH_MAX);	/* PATH_MAX includes '\0' */
449433d6423SLionel Sambuc   link_path[PATH_MAX - 1] = '\0';
450433d6423SLionel Sambuc   if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
451433d6423SLionel Sambuc 
452433d6423SLionel Sambuc   /* Make sure this is a symbolic link */
453433d6423SLionel Sambuc   if (!S_ISLNK(vp->v_mode))
454433d6423SLionel Sambuc 	r = EINVAL;
455433d6423SLionel Sambuc   else
456433d6423SLionel Sambuc 	r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path,
457433d6423SLionel Sambuc 		       PATH_MAX - 1, 1);
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc   if (r > 0) link_path[r] = '\0';	/* Terminate string when succesful */
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc   unlock_vnode(vp);
462433d6423SLionel Sambuc   unlock_vmnt(vmp);
463433d6423SLionel Sambuc   put_vnode(vp);
464433d6423SLionel Sambuc 
465433d6423SLionel Sambuc   return r;
466433d6423SLionel Sambuc }
467433d6423SLionel Sambuc 
468433d6423SLionel Sambuc /*===========================================================================*
469433d6423SLionel Sambuc  *                             do_rdlink				     *
470433d6423SLionel Sambuc  *===========================================================================*/
do_rdlink(void)471433d6423SLionel Sambuc int do_rdlink(void)
472433d6423SLionel Sambuc {
473433d6423SLionel Sambuc /* Perform the readlink(name, buf, bufsize) system call. */
474433d6423SLionel Sambuc   int r;
475433d6423SLionel Sambuc   struct vnode *vp;
476433d6423SLionel Sambuc   struct vmnt *vmp;
477433d6423SLionel Sambuc   char fullpath[PATH_MAX];
478433d6423SLionel Sambuc   struct lookup resolve;
479433d6423SLionel Sambuc   vir_bytes vname;
480433d6423SLionel Sambuc   size_t vname_length, buf_size;
481433d6423SLionel Sambuc   vir_bytes buf;
482433d6423SLionel Sambuc 
483433d6423SLionel Sambuc   vname = job_m_in.m_lc_vfs_readlink.name;
484433d6423SLionel Sambuc   vname_length = job_m_in.m_lc_vfs_readlink.namelen;
485433d6423SLionel Sambuc   buf = job_m_in.m_lc_vfs_readlink.buf;
486433d6423SLionel Sambuc   buf_size = job_m_in.m_lc_vfs_readlink.bufsize;
487433d6423SLionel Sambuc   if (buf_size > SSIZE_MAX) return(EINVAL);
488433d6423SLionel Sambuc 
489433d6423SLionel Sambuc   lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp);
490433d6423SLionel Sambuc   resolve.l_vmnt_lock = VMNT_READ;
491433d6423SLionel Sambuc   resolve.l_vnode_lock = VNODE_READ;
492433d6423SLionel Sambuc 
493433d6423SLionel Sambuc   /* Temporarily open the file containing the symbolic link */
494433d6423SLionel Sambuc   if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code);
495433d6423SLionel Sambuc   if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
496433d6423SLionel Sambuc 
497433d6423SLionel Sambuc   /* Make sure this is a symbolic link */
498433d6423SLionel Sambuc   if (!S_ISLNK(vp->v_mode))
499433d6423SLionel Sambuc 	r = EINVAL;
500433d6423SLionel Sambuc   else
501433d6423SLionel Sambuc 	r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0);
502433d6423SLionel Sambuc 
503433d6423SLionel Sambuc   unlock_vnode(vp);
504433d6423SLionel Sambuc   unlock_vmnt(vmp);
505433d6423SLionel Sambuc   put_vnode(vp);
506433d6423SLionel Sambuc 
507433d6423SLionel Sambuc   return(r);
508433d6423SLionel Sambuc }
509