7 #include "puffs_priv.h"
12 /*===========================================================================*
14 *===========================================================================*/
18 struct puffs_node
*pn
;
20 PUFFS_MAKECRED(pcr
, &global_kcred
);
22 if ((pn
= puffs_pn_nodewalk(global_pu
, 0, &fs_m_in
.m_vfs_fs_ftrunc
.inode
)) == NULL
)
25 start
= fs_m_in
.m_vfs_fs_ftrunc
.trc_start
;
26 end
= fs_m_in
.m_vfs_fs_ftrunc
.trc_end
;
31 if (pn
->pn_va
.va_size
== (u_quad_t
) start
)
34 if (global_pu
->pu_ops
.puffs_node_setattr
== NULL
)
37 puffs_vattr_null(&va
);
40 r
= global_pu
->pu_ops
.puffs_node_setattr(global_pu
, pn
, &va
, pcr
);
41 if (r
) return(EINVAL
);
43 /* XXX zerofill the given region. Can we make a hole? */
44 off_t bytes_left
= end
- start
;
47 if (global_pu
->pu_ops
.puffs_node_write
== NULL
)
50 /* XXX split into chunks? */
51 rw_buf
= malloc(bytes_left
);
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);
59 if (r
) return(EINVAL
);
62 update_timens(pn
, CTIME
| MTIME
, NULL
);
68 /*===========================================================================*
70 *===========================================================================*/
73 /* Perform the link(name1, name2) system call. */
76 char string
[NAME_MAX
+ 1];
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
)
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)
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
)
100 /* Check to see if the file has maximum number of links already. */
101 if (pn
->pn_va
.va_nlink
>= LINK_MAX
)
104 /* Only super_user may link to directories. */
105 if ((pn
->pn_va
.va_mode
& I_TYPE
) == I_DIRECTORY
&& caller_uid
!= SU_UID
)
108 if ((pn_dir
= puffs_pn_nodewalk(global_pu
, 0, &fs_m_in
.m_vfs_fs_link
.dir_ino
)) == NULL
)
111 if (pn_dir
->pn_va
.va_nlink
== NO_LINK
) {
112 /* Dir does not actually exist */
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
) {
119 if (r
== ENOENT
) r
= OK
;
124 if (r
!= OK
) return(r
);
127 pcn
.pcn_namelen
= strlen(string
);
128 assert(pcn
.pcn_namelen
<= MAXPATHLEN
);
129 strcpy(pcn
.pcn_name
, string
);
132 if (puffs_path_pcnbuild(global_pu
, &pcn
, pn_dir
) != 0)
136 if (global_pu
->pu_ops
.puffs_node_link(global_pu
, pn_dir
, pn
, &pcn
) != 0)
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
);
152 /*===========================================================================*
154 *===========================================================================*/
157 register int r
; /* return value */
159 struct puffs_node
*pn
;
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
)
171 if (!S_ISLNK(pn
->pn_va
.va_mode
))
174 if (global_pu
->pu_ops
.puffs_node_readlink
== NULL
)
177 r
= global_pu
->pu_ops
.puffs_node_readlink(global_pu
, pn
, pcr
, path
,
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
);
187 fs_m_out
.m_fs_vfs_rdlink
.nbytes
= copylen
;
193 /*===========================================================================*
195 *===========================================================================*/
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 */
211 struct timespec cur_time
;
213 if (global_pu
->pu_ops
.puffs_node_rename
== NULL
)
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
))
243 old_ip
= advance(old_dirp
, pcn_src
.pcn_name
, IGN_PERM
);
249 if (r
== EENTERMOUNT
|| r
== ELEAVEMOUNT
) {
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
))
260 if (new_dirp
->pn_va
.va_nlink
== NO_LINK
) {
261 /* Dir does not actually exist */
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
) {
276 if (old_ip
!= NULL
) {
278 odir
= ((old_ip
->pn_va
.va_mode
& I_TYPE
) == I_DIRECTORY
);
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) {
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
) {
304 if (old_ip
== new_ip
) r
= SAME
; /* old=new */
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
;
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.
334 pcn_src
.pcn_po_full
= old_ip
->pn_po
;
336 if (puffs_path_pcnbuild(global_pu
, &pcn_targ
, new_dirp
) != 0)
340 r
= global_pu
->pu_ops
.puffs_node_rename(global_pu
, old_dirp
, old_ip
, &pcn_src
,
341 new_dirp
, new_ip
, &pcn_targ
);
346 global_pu
->pu_pathfree(global_pu
, &pcn_targ
.pcn_po_full
);
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
);
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
;
365 if (puffs_pn_nodewalk(global_pu
, puffs_path_prefixadj
, &pi
)
371 global_pu
->pu_pathfree(global_pu
, &po_old
);
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
);
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 /*===========================================================================*
395 *===========================================================================*/
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.
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
);
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
,
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
)
425 /* The last directory exists. Does the file also exist? */
426 pn
= advance(pn_dir
, pcn
.pcn_name
, IGN_PERM
);
429 /* If error, return pnode. */
432 if (r
== EENTERMOUNT
|| r
== ELEAVEMOUNT
) {
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
)
444 r
= unlink_file(pn_dir
, pn
, &pcn
);
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
);
467 /*===========================================================================*
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
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
;
493 if (global_pu
->pu_ops
.puffs_node_rmdir
== NULL
)
496 if (!S_ISDIR(pn
->pn_va
.va_mode
))
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)
508 if (pn
->pn_va
.va_fileid
== global_pu
->pu_pn_root
->pn_va
.va_fileid
)
509 return(EBUSY
); /* can't remove 'root' */
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
);
526 /*===========================================================================*
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' */
540 if (global_pu
->pu_ops
.puffs_node_remove
== NULL
)
543 if (S_ISDIR(pn
->pn_va
.va_mode
))
547 r
= puffs_path_pcnbuild(global_pu
, pcn
, dirp
);
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
);