tools/llvm: Do not build with symbols
[minix3.git] / lib / libpuffs / link.c
blob99740ade2efe753151adc6fdfde24dcb953fa508
1 #include "fs.h"
3 #include <stdlib.h>
4 #include <assert.h>
6 #include "puffs.h"
7 #include "puffs_priv.h"
9 #define SAME 1000
12 /*===========================================================================*
13 * fs_ftrunc *
14 *===========================================================================*/
15 int fs_ftrunc(void)
17 int r;
18 struct puffs_node *pn;
19 off_t start, end;
20 PUFFS_MAKECRED(pcr, &global_kcred);
22 if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_ftrunc.inode)) == NULL)
23 return(EINVAL);
25 start = fs_m_in.m_vfs_fs_ftrunc.trc_start;
26 end = fs_m_in.m_vfs_fs_ftrunc.trc_end;
28 if (end == 0) {
29 struct vattr va;
31 if (pn->pn_va.va_size == (u_quad_t) start)
32 return(OK);
34 if (global_pu->pu_ops.puffs_node_setattr == NULL)
35 return(EINVAL);
37 puffs_vattr_null(&va);
38 va.va_size = start;
40 r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr);
41 if (r) return(EINVAL);
42 } else {
43 /* XXX zerofill the given region. Can we make a hole? */
44 off_t bytes_left = end - start;
45 char* rw_buf;
47 if (global_pu->pu_ops.puffs_node_write == NULL)
48 return(EINVAL);
50 /* XXX split into chunks? */
51 rw_buf = malloc(bytes_left);
52 if (!rw_buf)
53 panic("fs_ftrunc: failed to allocated memory\n");
54 memset(rw_buf, 0, bytes_left);
56 r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf,
57 start, (size_t *) &bytes_left, pcr, 0);
58 free(rw_buf);
59 if (r) return(EINVAL);
62 update_timens(pn, CTIME | MTIME, NULL);
64 return(r);
68 /*===========================================================================*
69 * fs_link *
70 *===========================================================================*/
71 int fs_link(void)
73 /* Perform the link(name1, name2) system call. */
75 register int r;
76 char string[NAME_MAX + 1];
77 phys_bytes len;
78 struct puffs_node *pn, *pn_dir, *new_pn;
79 struct timespec cur_time;
80 struct puffs_kcn pkcnp;
81 PUFFS_MAKECRED(pcr, &global_kcred);
82 struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) __UNCONST(pcr), {0,0,0}};
84 if (global_pu->pu_ops.puffs_node_link == NULL)
85 return(OK);
87 /* Copy the link name's last component */
88 len = fs_m_in.m_vfs_fs_link.path_len;
89 if (len > NAME_MAX + 1)
90 return(ENAMETOOLONG);
92 r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_link.grant, 0,
93 (vir_bytes) string, (size_t) len);
94 if (r != OK) return(r);
95 NUL(string, len, sizeof(string));
97 if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_link.inode)) == NULL)
98 return(EINVAL);
100 /* Check to see if the file has maximum number of links already. */
101 if (pn->pn_va.va_nlink >= LINK_MAX)
102 return(EMLINK);
104 /* Only super_user may link to directories. */
105 if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY && caller_uid != SU_UID)
106 return(EPERM);
108 if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_link.dir_ino)) == NULL)
109 return(EINVAL);
111 if (pn_dir->pn_va.va_nlink == NO_LINK) {
112 /* Dir does not actually exist */
113 return(ENOENT);
116 /* If 'name2' exists in full (even if no space) set 'r' to error. */
117 if ((new_pn = advance(pn_dir, string, IGN_PERM)) == NULL) {
118 r = err_code;
119 if (r == ENOENT) r = OK;
120 } else {
121 r = EEXIST;
124 if (r != OK) return(r);
126 /* Try to link. */
127 pcn.pcn_namelen = strlen(string);
128 assert(pcn.pcn_namelen <= MAXPATHLEN);
129 strcpy(pcn.pcn_name, string);
131 if (buildpath) {
132 if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0)
133 return(EINVAL);
136 if (global_pu->pu_ops.puffs_node_link(global_pu, pn_dir, pn, &pcn) != 0)
137 r = EINVAL;
139 if (buildpath)
140 global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
142 if (r != OK) return(EINVAL);
144 cur_time = clock_timespec();
145 update_timens(pn, CTIME, &cur_time);
146 update_timens(pn_dir, MTIME | CTIME, &cur_time);
148 return(OK);
152 /*===========================================================================*
153 * fs_rdlink *
154 *===========================================================================*/
155 int fs_rdlink(void)
157 register int r; /* return value */
158 size_t copylen;
159 struct puffs_node *pn;
160 char path[PATH_MAX];
161 PUFFS_MAKECRED(pcr, &global_kcred);
163 copylen = fs_m_in.m_vfs_fs_rdlink.mem_size < UMAX_FILE_POS ?
164 fs_m_in.m_vfs_fs_rdlink.mem_size : UMAX_FILE_POS;
166 assert(copylen <= PATH_MAX);
168 if ((pn = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_rdlink.inode)) == NULL)
169 return(EINVAL);
171 if (!S_ISLNK(pn->pn_va.va_mode))
172 return(EACCES);
174 if (global_pu->pu_ops.puffs_node_readlink == NULL)
175 return(EINVAL);
177 r = global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, path,
178 &copylen);
179 if (r != OK) {
180 if (r > 0) r = -r;
181 return(r);
184 r = sys_safecopyto(VFS_PROC_NR, fs_m_in.m_vfs_fs_rdlink.grant,
185 (vir_bytes) 0, (vir_bytes) path, (size_t) copylen);
186 if (r == OK)
187 fs_m_out.m_fs_vfs_rdlink.nbytes = copylen;
189 return(r);
193 /*===========================================================================*
194 * fs_rename *
195 *===========================================================================*/
196 int fs_rename(void)
198 /* Perform the rename(name1, name2) system call. */
199 struct puffs_node *old_dirp, *old_ip; /* ptrs to old dir, file pnodes */
200 struct puffs_node *new_dirp, *new_ip; /* ptrs to new dir, file pnodes */
201 struct puffs_kcn pkcnp_src;
202 PUFFS_MAKECRED(pcr_src, &global_kcred);
203 struct puffs_cn pcn_src = {&pkcnp_src, (struct puffs_cred *) __UNCONST(pcr_src), {0,0,0}};
204 struct puffs_kcn pkcnp_dest;
205 PUFFS_MAKECRED(pcr_dest, &global_kcred);
206 struct puffs_cn pcn_targ = {&pkcnp_dest, (struct puffs_cred *) __UNCONST(pcr_dest), {0,0,0}};
207 int r = OK; /* error flag; initially no error */
208 int odir, ndir; /* TRUE iff {old|new} file is dir */
209 int same_pdir; /* TRUE iff parent dirs are the same */
210 phys_bytes len;
211 struct timespec cur_time;
213 if (global_pu->pu_ops.puffs_node_rename == NULL)
214 return(EINVAL);
216 /* Copy the last component of the old name */
217 len = fs_m_in.m_vfs_fs_rename.len_old; /* including trailing '\0' */
218 if (len > NAME_MAX + 1)
219 return(ENAMETOOLONG);
221 r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_rename.grant_old,
222 (vir_bytes) 0, (vir_bytes) pcn_src.pcn_name, (size_t) len);
223 if (r != OK) return(r);
224 NUL(pcn_src.pcn_name, len, sizeof(pcn_src.pcn_name));
225 pcn_src.pcn_namelen = len - 1;
227 /* Copy the last component of the new name */
228 len = fs_m_in.m_vfs_fs_rename.len_new; /* including trailing '\0' */
229 if (len > NAME_MAX + 1)
230 return(ENAMETOOLONG);
232 r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_rename.grant_new,
233 (vir_bytes) 0, (vir_bytes) pcn_targ.pcn_name, (size_t) len);
234 if (r != OK) return(r);
235 NUL(pcn_targ.pcn_name, len, sizeof(pcn_targ.pcn_name));
236 pcn_targ.pcn_namelen = len - 1;
238 /* Get old dir pnode */
239 if ((old_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_rename.dir_old))
240 == NULL)
241 return(ENOENT);
243 old_ip = advance(old_dirp, pcn_src.pcn_name, IGN_PERM);
244 if (!old_ip) {
245 return(ENOENT);
247 r = err_code;
249 if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
250 old_ip = NULL;
251 if (r == EENTERMOUNT) r = EXDEV; /* should this fail at all? */
252 else if (r == ELEAVEMOUNT) r = EINVAL; /* rename on dot-dot */
255 /* Get new dir pnode */
256 if ((new_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_rename.dir_new))
257 == NULL) {
258 r = ENOENT;
259 } else {
260 if (new_dirp->pn_va.va_nlink == NO_LINK) {
261 /* Dir does not actually exist */
262 return(ENOENT);
266 /* not required to exist */
267 new_ip = advance(new_dirp, pcn_targ.pcn_name, IGN_PERM);
269 /* However, if the check failed because the file does exist, don't continue.
270 * Note that ELEAVEMOUNT is covered by the dot-dot check later. */
271 if (err_code == EENTERMOUNT) {
272 new_ip = NULL;
273 r = EBUSY;
276 if (old_ip != NULL) {
277 /* TRUE iff dir */
278 odir = ((old_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
279 } else {
280 odir = FALSE;
283 if (r != OK) return(r);
285 /* Check for a variety of possible errors. */
286 same_pdir = (old_dirp == new_dirp);
288 /* The old or new name must not be . or .. */
289 if (strcmp(pcn_src.pcn_name, ".") == 0 ||
290 strcmp(pcn_src.pcn_name, "..") == 0 ||
291 strcmp(pcn_targ.pcn_name, ".") == 0 ||
292 strcmp(pcn_targ.pcn_name, "..") == 0) {
293 r = EINVAL;
296 /* Some tests apply only if the new path exists. */
297 if (new_ip == NULL) {
298 if (odir && (new_dirp->pn_va.va_nlink >= SHRT_MAX ||
299 new_dirp->pn_va.va_nlink >= LINK_MAX) &&
300 !same_pdir && r == OK) {
301 r = EMLINK;
303 } else {
304 if (old_ip == new_ip) r = SAME; /* old=new */
306 /* dir ? */
307 ndir = ((new_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
308 if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
309 if (odir == FALSE && ndir == TRUE) r = EISDIR;
312 if (r == SAME) {
313 r = OK;
314 goto rename_out;
317 if (r != OK) return(r);
319 /* If a process has another root directory than the system root, we might
320 * "accidently" be moving it's working directory to a place where it's
321 * root directory isn't a super directory of it anymore. This can make
322 * the function chroot useless. If chroot will be used often we should
323 * probably check for it here. */
325 /* The rename will probably work. Only two things can go wrong now:
326 * 1. being unable to remove the new file. (when new file already exists)
327 * 2. being unable to make the new directory entry. (new file doesn't exists)
328 * [directory has to grow by one block and cannot because the disk
329 * is completely full].
330 * 3. Something (doubtfully) else depending on the FS.
333 if (buildpath) {
334 pcn_src.pcn_po_full = old_ip->pn_po;
336 if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0)
337 return(EINVAL);
340 r = global_pu->pu_ops.puffs_node_rename(global_pu, old_dirp, old_ip, &pcn_src,
341 new_dirp, new_ip, &pcn_targ);
342 if (r > 0) r = -r;
344 if (buildpath) {
345 if (r) {
346 global_pu->pu_pathfree(global_pu, &pcn_targ.pcn_po_full);
347 } else {
348 struct puffs_pathinfo pi;
349 struct puffs_pathobj po_old;
351 /* handle this node */
352 po_old = old_ip->pn_po;
353 old_ip->pn_po = pcn_targ.pcn_po_full;
355 if (old_ip->pn_va.va_type != VDIR) {
356 global_pu->pu_pathfree(global_pu, &po_old);
357 return(OK);
360 /* handle all child nodes for DIRs */
361 pi.pi_old = &pcn_src.pcn_po_full;
362 pi.pi_new = &pcn_targ.pcn_po_full;
364 PU_LOCK();
365 if (puffs_pn_nodewalk(global_pu, puffs_path_prefixadj, &pi)
366 != NULL) {
367 /* Actually nomem */
368 return(EINVAL);
370 PU_UNLOCK();
371 global_pu->pu_pathfree(global_pu, &po_old);
375 rename_out:
376 cur_time = clock_timespec();
377 update_timens(old_dirp, MTIME | CTIME, &cur_time);
378 update_timens(new_dirp, MTIME | CTIME, &cur_time);
380 /* XXX see release_node comment in fs_unlink */
381 if (new_ip && new_ip->pn_count == 0) {
382 release_node(global_pu, new_ip);
385 return(r);
388 static int remove_dir(struct puffs_node *pn_dir, struct puffs_node *pn,
389 struct puffs_cn *pcn);
390 static int unlink_file(struct puffs_node *dirp, struct puffs_node *pn,
391 struct puffs_cn *pcn);
393 /*===========================================================================*
394 * fs_unlink *
395 *===========================================================================*/
396 int fs_unlink(void)
398 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
399 * is almost the same. They differ only in some condition testing. Unlink()
400 * may be used by the superuser to do dangerous things; rmdir() may not.
402 int r;
403 struct puffs_node *pn, *pn_dir;
404 struct timespec cur_time;
405 struct puffs_kcn pkcnp;
406 struct puffs_cn pcn = {&pkcnp, 0, {0,0,0}};
407 PUFFS_KCREDTOCRED(pcn.pcn_cred, &global_kcred);
408 int len;
410 /* Copy the last component */
411 len = fs_m_in.m_vfs_fs_unlink.path_len;
412 pcn.pcn_namelen = len - 1;
413 if (pcn.pcn_namelen > NAME_MAX)
414 return(ENAMETOOLONG);
416 r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_unlink.grant,
417 (vir_bytes) 0, (vir_bytes) pcn.pcn_name,
418 (size_t) len);
419 if (r != OK) return (r);
420 NUL(pcn.pcn_name, len, sizeof(pcn.pcn_name));
422 if ((pn_dir = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.m_vfs_fs_unlink.inode)) == NULL)
423 return(EINVAL);
425 /* The last directory exists. Does the file also exist? */
426 pn = advance(pn_dir, pcn.pcn_name, IGN_PERM);
427 r = err_code;
429 /* If error, return pnode. */
430 if (r != OK) {
431 /* Mount point? */
432 if (r == EENTERMOUNT || r == ELEAVEMOUNT) {
433 r = EBUSY;
435 return(r);
438 /* Now test if the call is allowed, separately for unlink() and rmdir(). */
439 if (fs_m_in.m_type == REQ_UNLINK) {
440 /* Only the su may unlink directories, but the su can unlink any dir */
441 if ((pn->pn_va.va_mode & I_TYPE) == I_DIRECTORY)
442 r = EPERM;
443 if (r == OK)
444 r = unlink_file(pn_dir, pn, &pcn);
445 } else {
446 r = remove_dir(pn_dir, pn, &pcn); /* call is RMDIR */
449 if (pn->pn_va.va_nlink != 0) {
450 cur_time = clock_timespec();
451 update_timens(pn, CTIME, &cur_time);
452 update_timens(pn_dir, MTIME | CTIME, &cur_time);
455 /* XXX Ideally, we should check pn->pn_flags & PUFFS_NODE_REMOVED, but
456 * librefuse doesn't set it (neither manually or via puffs_pn_remove() ).
457 * Thus we just check that "pn_count == 0". Otherwise release_node()
458 * will be called in fs_put().
460 if (pn->pn_count == 0)
461 release_node(global_pu, pn);
463 return(r);
467 /*===========================================================================*
468 * remove_dir *
469 *===========================================================================*/
470 static int remove_dir(
471 struct puffs_node *pn_dir, /* parent directory */
472 struct puffs_node *pn, /* directory to be removed */
473 struct puffs_cn *pcn /* Name, creads of directory */
476 /* A directory file has to be removed. Five conditions have to met:
477 * - The file must be a directory
478 * - The directory must be empty (except for . and ..)
479 * - The final component of the path must not be . or ..
480 * - The directory must not be the root of a mounted file system (VFS)
481 * - The directory must not be anybody's root/working directory (VFS)
484 /* "." and ".." dentries can be stored in 28 bytes */
485 #define EMPTY_DIR_DENTRIES_SIZE 28
486 int r;
487 char remove_dir_buf[EMPTY_DIR_DENTRIES_SIZE];
488 struct dirent *dent = (struct dirent*) remove_dir_buf;
489 int buf_left = EMPTY_DIR_DENTRIES_SIZE;
490 off_t pos = 0;
491 int eofflag = 0;
493 if (global_pu->pu_ops.puffs_node_rmdir == NULL)
494 return(EINVAL);
496 if (!S_ISDIR(pn->pn_va.va_mode))
497 return(ENOTDIR);
499 /* Check if directory is empty */
500 r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos,
501 (size_t *)&buf_left, pcn->pcn_cred, &eofflag, 0, 0);
502 if (r) return(EINVAL);
503 if (!eofflag) return(ENOTEMPTY);
505 if (strcmp(pcn->pcn_name, ".") == 0 || strcmp(pcn->pcn_name, "..") == 0)
506 return(EINVAL);
508 if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid)
509 return(EBUSY); /* can't remove 'root' */
511 if (buildpath) {
512 r = puffs_path_pcnbuild(global_pu, pcn, pn_dir);
513 if (r) return(EINVAL);
516 r = global_pu->pu_ops.puffs_node_rmdir(global_pu, pn_dir, pn, pcn);
518 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
520 if (r) return(EINVAL);
522 return(OK);
526 /*===========================================================================*
527 * unlink_file *
528 *===========================================================================*/
529 static int unlink_file(
530 struct puffs_node *dirp, /* parent directory of file */
531 struct puffs_node *pn, /* pnode of file, may be NULL too. */
532 struct puffs_cn *pcn /* Name, creads of file */
535 /* Unlink 'file_name'; pn must be the pnode of 'file_name' */
536 int r;
538 assert(pn != NULL);
540 if (global_pu->pu_ops.puffs_node_remove == NULL)
541 return(EINVAL);
543 if (S_ISDIR(pn->pn_va.va_mode))
544 return(EINVAL);
546 if (buildpath) {
547 r = puffs_path_pcnbuild(global_pu, pcn, dirp);
548 if (r)
549 return(EINVAL);
552 r = global_pu->pu_ops.puffs_node_remove(global_pu, dirp, pn, pcn);
554 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
556 if (r) return(EINVAL);
558 return(OK);