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