Remove building with NOCRYPTO option
[minix.git] / minix / lib / libpuffs / link.c
blobb55ed00ee6637bf8139a4d4a49a68d31b9f249f0
1 #include "fs.h"
3 /*===========================================================================*
4 * fs_trunc *
5 *===========================================================================*/
6 int fs_trunc(ino_t ino_nr, off_t start, off_t end)
8 int r;
9 struct puffs_node *pn;
10 PUFFS_MAKECRED(pcr, &global_kcred);
12 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL)
13 return(EINVAL);
15 if (end == 0) {
16 struct vattr va;
18 if (pn->pn_va.va_size == (u_quad_t) start)
19 return(OK);
21 if (global_pu->pu_ops.puffs_node_setattr == NULL)
22 return(EINVAL);
24 puffs_vattr_null(&va);
25 va.va_size = start;
27 r = global_pu->pu_ops.puffs_node_setattr(global_pu, pn, &va, pcr);
28 if (r) return(EINVAL);
29 } else {
30 /* XXX zerofill the given region. Can we make a hole? */
31 off_t bytes_left = end - start;
32 char* rw_buf;
34 if (global_pu->pu_ops.puffs_node_write == NULL)
35 return(EINVAL);
37 /* XXX split into chunks? */
38 rw_buf = malloc(bytes_left);
39 if (!rw_buf)
40 panic("fs_ftrunc: failed to allocated memory\n");
41 memset(rw_buf, 0, bytes_left);
43 r = global_pu->pu_ops.puffs_node_write(global_pu, pn, (uint8_t *)rw_buf,
44 start, (size_t *) &bytes_left, pcr, 0);
45 free(rw_buf);
46 if (r) return(EINVAL);
49 update_timens(pn, CTIME | MTIME, NULL);
51 return(r);
55 /*===========================================================================*
56 * fs_link *
57 *===========================================================================*/
58 int fs_link(ino_t dir_nr, char *name, ino_t ino_nr)
60 /* Perform the link(name1, name2) system call. */
62 register int r;
63 struct puffs_node *pn, *pn_dir, *new_pn;
64 struct timespec cur_time;
65 struct puffs_kcn pkcnp;
66 PUFFS_MAKECRED(pcr, &global_kcred);
67 struct puffs_cn pcn = {&pkcnp, (struct puffs_cred *) __UNCONST(pcr), {0,0,0}};
69 if (global_pu->pu_ops.puffs_node_link == NULL)
70 return(OK);
72 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL)
73 return(EINVAL);
75 /* Check to see if the file has maximum number of links already. */
76 if (pn->pn_va.va_nlink >= LINK_MAX)
77 return(EMLINK);
79 /* Linking directories is too dangerous to allow. */
80 if (S_ISDIR(pn->pn_va.va_mode))
81 return(EPERM);
83 if ((pn_dir = puffs_pn_nodewalk(global_pu, find_inode_cb, &dir_nr)) == NULL)
84 return(EINVAL);
86 if (pn_dir->pn_va.va_nlink == NO_LINK) {
87 /* Dir does not actually exist */
88 return(ENOENT);
91 /* If 'name2' exists in full (even if no space) set 'r' to error. */
92 if ((new_pn = advance(pn_dir, name)) == NULL) {
93 r = err_code;
94 if (r == ENOENT) r = OK;
95 } else {
96 r = EEXIST;
99 if (r != OK) return(r);
101 /* Try to link. */
102 pcn.pcn_namelen = strlen(name);
103 assert(pcn.pcn_namelen <= NAME_MAX);
104 strcpy(pcn.pcn_name, name);
106 if (buildpath) {
107 if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0)
108 return(EINVAL);
111 if (global_pu->pu_ops.puffs_node_link(global_pu, pn_dir, pn, &pcn) != 0)
112 r = EINVAL;
114 if (buildpath)
115 global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full);
117 if (r != OK) return(EINVAL);
119 (void)clock_time(&cur_time);
120 update_timens(pn, CTIME, &cur_time);
121 update_timens(pn_dir, MTIME | CTIME, &cur_time);
123 return(OK);
127 /*===========================================================================*
128 * fs_rdlink *
129 *===========================================================================*/
130 ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes)
132 register int r; /* return value */
133 struct puffs_node *pn;
134 char path[PATH_MAX];
135 PUFFS_MAKECRED(pcr, &global_kcred);
137 if (bytes > sizeof(path))
138 bytes = sizeof(path);
140 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL)
141 return(EINVAL);
143 if (!S_ISLNK(pn->pn_va.va_mode))
144 return(EACCES);
146 if (global_pu->pu_ops.puffs_node_readlink == NULL)
147 return(EINVAL);
149 r = global_pu->pu_ops.puffs_node_readlink(global_pu, pn, pcr, path, &bytes);
150 if (r != OK) {
151 if (r > 0) r = -r;
152 return(r);
155 r = fsdriver_copyout(data, 0, path, bytes);
157 return (r == OK) ? (ssize_t)bytes : r;
161 /*===========================================================================*
162 * fs_rename *
163 *===========================================================================*/
164 int fs_rename(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr,
165 char *new_name)
167 /* Perform the rename(name1, name2) system call. */
168 struct puffs_node *old_dirp, *old_ip; /* ptrs to old dir, file pnodes */
169 struct puffs_node *new_dirp, *new_ip; /* ptrs to new dir, file pnodes */
170 struct puffs_kcn pkcnp_src;
171 PUFFS_MAKECRED(pcr_src, &global_kcred);
172 struct puffs_cn pcn_src = {&pkcnp_src, (struct puffs_cred *) __UNCONST(pcr_src), {0,0,0}};
173 struct puffs_kcn pkcnp_dest;
174 PUFFS_MAKECRED(pcr_dest, &global_kcred);
175 struct puffs_cn pcn_targ = {&pkcnp_dest, (struct puffs_cred *) __UNCONST(pcr_dest), {0,0,0}};
176 int r = OK; /* error flag; initially no error */
177 int odir, ndir; /* TRUE iff {old|new} file is dir */
178 int same_pdir; /* TRUE iff parent dirs are the same */
179 struct timespec cur_time;
181 if (global_pu->pu_ops.puffs_node_rename == NULL)
182 return(EINVAL);
184 /* Copy the last component of the old name */
185 pcn_src.pcn_namelen = strlen(old_name);
186 assert(pcn_src.pcn_namelen <= NAME_MAX);
187 strcpy(pcn_src.pcn_name, old_name);
189 /* Copy the last component of the new name */
190 pcn_targ.pcn_namelen = strlen(new_name);
191 assert(pcn_targ.pcn_namelen <= NAME_MAX);
192 strcpy(pcn_targ.pcn_name, new_name);
194 /* Get old dir pnode */
195 if ((old_dirp = puffs_pn_nodewalk(global_pu, find_inode_cb,
196 &old_dir_nr)) == NULL)
197 return(ENOENT);
199 old_ip = advance(old_dirp, pcn_src.pcn_name);
200 if (!old_ip)
201 return(err_code);
203 if (old_ip->pn_mountpoint)
204 return(EBUSY);
206 /* Get new dir pnode */
207 if ((new_dirp = puffs_pn_nodewalk(global_pu, find_inode_cb,
208 &new_dir_nr)) == NULL) {
209 return(ENOENT);
210 } else {
211 if (new_dirp->pn_va.va_nlink == NO_LINK) {
212 /* Dir does not actually exist */
213 return(ENOENT);
217 /* not required to exist */
218 new_ip = advance(new_dirp, pcn_targ.pcn_name);
220 /* If the node does exist, make sure it's not a mountpoint. */
221 if (new_ip != NULL && new_ip->pn_mountpoint)
222 return(EBUSY);
224 if (old_ip != NULL) {
225 /* TRUE iff dir */
226 odir = ((old_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
227 } else {
228 odir = FALSE;
231 /* Check for a variety of possible errors. */
232 same_pdir = (old_dirp == new_dirp);
234 /* Some tests apply only if the new path exists. */
235 if (new_ip == NULL) {
236 if (odir && (new_dirp->pn_va.va_nlink >= SHRT_MAX ||
237 new_dirp->pn_va.va_nlink >= LINK_MAX) && !same_pdir) {
238 return(EMLINK);
240 } else {
241 if (old_ip == new_ip) /* old=new */
242 return(OK); /* do NOT update directory times in this case */
244 /* dir ? */
245 ndir = ((new_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY);
246 if (odir == TRUE && ndir == FALSE) return(ENOTDIR);
247 if (odir == FALSE && ndir == TRUE) return(EISDIR);
250 /* If a process has another root directory than the system root, we might
251 * "accidently" be moving it's working directory to a place where it's
252 * root directory isn't a super directory of it anymore. This can make
253 * the function chroot useless. If chroot will be used often we should
254 * probably check for it here. */
256 /* The rename will probably work. Only two things can go wrong now:
257 * 1. being unable to remove the new file. (when new file already exists)
258 * 2. being unable to make the new directory entry. (new file doesn't exists)
259 * [directory has to grow by one block and cannot because the disk
260 * is completely full].
261 * 3. Something (doubtfully) else depending on the FS.
264 if (buildpath) {
265 pcn_src.pcn_po_full = old_ip->pn_po;
267 if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0)
268 return(EINVAL);
271 r = global_pu->pu_ops.puffs_node_rename(global_pu, old_dirp, old_ip, &pcn_src,
272 new_dirp, new_ip, &pcn_targ);
273 if (r > 0) r = -r;
275 if (buildpath) {
276 if (r) {
277 global_pu->pu_pathfree(global_pu, &pcn_targ.pcn_po_full);
278 } else {
279 struct puffs_pathinfo pi;
280 struct puffs_pathobj po_old;
282 /* handle this node */
283 po_old = old_ip->pn_po;
284 old_ip->pn_po = pcn_targ.pcn_po_full;
286 if (old_ip->pn_va.va_type != VDIR) {
287 global_pu->pu_pathfree(global_pu, &po_old);
288 return(OK);
291 /* handle all child nodes for DIRs */
292 pi.pi_old = &pcn_src.pcn_po_full;
293 pi.pi_new = &pcn_targ.pcn_po_full;
295 PU_LOCK();
296 if (puffs_pn_nodewalk(global_pu, puffs_path_prefixadj, &pi)
297 != NULL) {
298 /* Actually nomem */
299 return(EINVAL);
301 PU_UNLOCK();
302 global_pu->pu_pathfree(global_pu, &po_old);
306 (void)clock_time(&cur_time);
307 update_timens(old_dirp, MTIME | CTIME, &cur_time);
308 update_timens(new_dirp, MTIME | CTIME, &cur_time);
310 /* XXX see release_node comment in fs_unlink */
311 if (new_ip && new_ip->pn_count == 0) {
312 release_node(global_pu, new_ip);
315 return(r);
318 static int remove_dir(struct puffs_node *pn_dir, struct puffs_node *pn,
319 struct puffs_cn *pcn);
320 static int unlink_file(struct puffs_node *dirp, struct puffs_node *pn,
321 struct puffs_cn *pcn);
323 /*===========================================================================*
324 * fs_unlink *
325 *===========================================================================*/
326 int fs_unlink(ino_t dir_nr, char *name, int call)
328 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
329 * is almost the same. They differ only in some condition testing.
331 int r;
332 struct puffs_node *pn, *pn_dir;
333 struct timespec cur_time;
334 struct puffs_kcn pkcnp;
335 struct puffs_cn pcn = {&pkcnp, 0, {0,0,0}};
336 PUFFS_KCREDTOCRED(pcn.pcn_cred, &global_kcred);
338 /* Copy the last component */
339 pcn.pcn_namelen = strlen(name);
340 assert(pcn.pcn_namelen <= NAME_MAX);
341 strcpy(pcn.pcn_name, name);
343 if ((pn_dir = puffs_pn_nodewalk(global_pu, find_inode_cb, &dir_nr)) == NULL)
344 return(EINVAL);
346 /* The last directory exists. Does the file also exist? */
347 pn = advance(pn_dir, pcn.pcn_name);
348 r = err_code;
350 /* If error, return pnode. */
351 if (r != OK)
352 return(r);
353 if (pn->pn_mountpoint)
354 return EBUSY;
356 /* Now test if the call is allowed, separately for unlink() and rmdir(). */
357 if (call == FSC_UNLINK) {
358 r = unlink_file(pn_dir, pn, &pcn);
359 } else {
360 r = remove_dir(pn_dir, pn, &pcn); /* call is RMDIR */
363 if (pn->pn_va.va_nlink != 0) {
364 (void)clock_time(&cur_time);
365 update_timens(pn, CTIME, &cur_time);
366 update_timens(pn_dir, MTIME | CTIME, &cur_time);
369 /* XXX Ideally, we should check pn->pn_flags & PUFFS_NODE_REMOVED, but
370 * librefuse doesn't set it (neither manually or via puffs_pn_remove() ).
371 * Thus we just check that "pn_count == 0". Otherwise release_node()
372 * will be called in fs_put().
374 if (pn->pn_count == 0)
375 release_node(global_pu, pn);
377 return(r);
381 /*===========================================================================*
382 * remove_dir *
383 *===========================================================================*/
384 static int remove_dir(
385 struct puffs_node *pn_dir, /* parent directory */
386 struct puffs_node *pn, /* directory to be removed */
387 struct puffs_cn *pcn /* Name, creads of directory */
390 /* A directory file has to be removed. Five conditions have to met:
391 * - The file must be a directory
392 * - The directory must be empty (except for . and ..)
393 * - The final component of the path must not be . or ..
394 * - The directory must not be the root of a mounted file system (VFS)
395 * - The directory must not be anybody's root/working directory (VFS)
398 /* "." and ".." dentries can be stored in 28 bytes */
399 #define EMPTY_DIR_DENTRIES_SIZE 28
400 int r;
401 char remove_dir_buf[EMPTY_DIR_DENTRIES_SIZE];
402 struct dirent *dent = (struct dirent*) remove_dir_buf;
403 int buf_left = EMPTY_DIR_DENTRIES_SIZE;
404 off_t pos = 0;
405 int eofflag = 0;
407 if (global_pu->pu_ops.puffs_node_rmdir == NULL)
408 return(EINVAL);
410 if (!S_ISDIR(pn->pn_va.va_mode))
411 return(ENOTDIR);
413 /* Check if directory is empty */
414 r = global_pu->pu_ops.puffs_node_readdir(global_pu, pn, dent, &pos,
415 (size_t *)&buf_left, pcn->pcn_cred, &eofflag, 0, 0);
416 if (r) return(EINVAL);
417 if (!eofflag) return(ENOTEMPTY);
419 if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid)
420 return(EBUSY); /* can't remove 'root' */
422 if (buildpath) {
423 r = puffs_path_pcnbuild(global_pu, pcn, pn_dir);
424 if (r) return(EINVAL);
427 r = global_pu->pu_ops.puffs_node_rmdir(global_pu, pn_dir, pn, pcn);
429 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
431 if (r) return(EINVAL);
433 return(OK);
437 /*===========================================================================*
438 * unlink_file *
439 *===========================================================================*/
440 static int unlink_file(
441 struct puffs_node *dirp, /* parent directory of file */
442 struct puffs_node *pn, /* pnode of file, may be NULL too. */
443 struct puffs_cn *pcn /* Name, creads of file */
446 /* Unlink 'file_name'; pn must be the pnode of 'file_name' */
447 int r;
449 assert(pn != NULL);
451 if (global_pu->pu_ops.puffs_node_remove == NULL)
452 return(EINVAL);
454 if (S_ISDIR(pn->pn_va.va_mode))
455 return(EPERM);
457 if (buildpath) {
458 r = puffs_path_pcnbuild(global_pu, pcn, dirp);
459 if (r)
460 return(EINVAL);
463 r = global_pu->pu_ops.puffs_node_remove(global_pu, dirp, pn, pcn);
465 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full);
467 if (r) return(EINVAL);
469 return(OK);