3 /*===========================================================================*
5 *===========================================================================*/
6 int fs_trunc(ino_t ino_nr
, off_t start
, off_t end
)
10 PUFFS_MAKECRED(pcr
, &global_kcred
);
12 if ((pn
= puffs_pn_nodewalk(global_pu
, find_inode_cb
, &ino_nr
)) == NULL
)
18 if (pn
->pn_va
.va_size
== (u_quad_t
) start
)
21 if (global_pu
->pu_ops
.puffs_node_setattr
== NULL
)
24 puffs_vattr_null(&va
);
27 r
= global_pu
->pu_ops
.puffs_node_setattr(global_pu
, pn
, &va
, pcr
);
28 if (r
) return(EINVAL
);
30 /* XXX zerofill the given region. Can we make a hole? */
31 off_t bytes_left
= end
- start
;
34 if (global_pu
->pu_ops
.puffs_node_write
== NULL
)
37 /* XXX split into chunks? */
38 rw_buf
= malloc(bytes_left
);
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);
46 if (r
) return(EINVAL
);
49 update_timens(pn
, CTIME
| MTIME
, NULL
);
55 /*===========================================================================*
57 *===========================================================================*/
58 int fs_link(ino_t dir_nr
, char *name
, ino_t ino_nr
)
60 /* Perform the link(name1, name2) system call. */
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
)
72 if ((pn
= puffs_pn_nodewalk(global_pu
, find_inode_cb
, &ino_nr
)) == NULL
)
75 /* Check to see if the file has maximum number of links already. */
76 if (pn
->pn_va
.va_nlink
>= LINK_MAX
)
79 /* Linking directories is too dangerous to allow. */
80 if (S_ISDIR(pn
->pn_va
.va_mode
))
83 if ((pn_dir
= puffs_pn_nodewalk(global_pu
, find_inode_cb
, &dir_nr
)) == NULL
)
86 if (pn_dir
->pn_va
.va_nlink
== NO_LINK
) {
87 /* Dir does not actually exist */
91 /* If 'name2' exists in full (even if no space) set 'r' to error. */
92 if ((new_pn
= advance(pn_dir
, name
)) == NULL
) {
94 if (r
== ENOENT
) r
= OK
;
99 if (r
!= OK
) return(r
);
102 pcn
.pcn_namelen
= strlen(name
);
103 assert(pcn
.pcn_namelen
<= NAME_MAX
);
104 strcpy(pcn
.pcn_name
, name
);
107 if (puffs_path_pcnbuild(global_pu
, &pcn
, pn_dir
) != 0)
111 if (global_pu
->pu_ops
.puffs_node_link(global_pu
, pn_dir
, pn
, &pcn
) != 0)
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
);
127 /*===========================================================================*
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
;
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
)
143 if (!S_ISLNK(pn
->pn_va
.va_mode
))
146 if (global_pu
->pu_ops
.puffs_node_readlink
== NULL
)
149 r
= global_pu
->pu_ops
.puffs_node_readlink(global_pu
, pn
, pcr
, path
, &bytes
);
155 r
= fsdriver_copyout(data
, 0, path
, bytes
);
157 return (r
== OK
) ? (ssize_t
)bytes
: r
;
161 /*===========================================================================*
163 *===========================================================================*/
164 int fs_rename(ino_t old_dir_nr
, char *old_name
, ino_t new_dir_nr
,
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
)
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
)
199 old_ip
= advance(old_dirp
, pcn_src
.pcn_name
);
203 if (old_ip
->pn_mountpoint
)
206 /* Get new dir pnode */
207 if ((new_dirp
= puffs_pn_nodewalk(global_pu
, find_inode_cb
,
208 &new_dir_nr
)) == NULL
) {
211 if (new_dirp
->pn_va
.va_nlink
== NO_LINK
) {
212 /* Dir does not actually exist */
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
)
224 if (old_ip
!= NULL
) {
226 odir
= ((old_ip
->pn_va
.va_mode
& I_TYPE
) == I_DIRECTORY
);
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
) {
241 if (old_ip
== new_ip
) /* old=new */
242 return(OK
); /* do NOT update directory times in this case */
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.
265 pcn_src
.pcn_po_full
= old_ip
->pn_po
;
267 if (puffs_path_pcnbuild(global_pu
, &pcn_targ
, new_dirp
) != 0)
271 r
= global_pu
->pu_ops
.puffs_node_rename(global_pu
, old_dirp
, old_ip
, &pcn_src
,
272 new_dirp
, new_ip
, &pcn_targ
);
277 global_pu
->pu_pathfree(global_pu
, &pcn_targ
.pcn_po_full
);
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
);
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
;
296 if (puffs_pn_nodewalk(global_pu
, puffs_path_prefixadj
, &pi
)
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
);
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 /*===========================================================================*
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.
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
)
346 /* The last directory exists. Does the file also exist? */
347 pn
= advance(pn_dir
, pcn
.pcn_name
);
350 /* If error, return pnode. */
353 if (pn
->pn_mountpoint
)
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
);
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
);
381 /*===========================================================================*
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
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
;
407 if (global_pu
->pu_ops
.puffs_node_rmdir
== NULL
)
410 if (!S_ISDIR(pn
->pn_va
.va_mode
))
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' */
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
);
437 /*===========================================================================*
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' */
451 if (global_pu
->pu_ops
.puffs_node_remove
== NULL
)
454 if (S_ISDIR(pn
->pn_va
.va_mode
))
458 r
= puffs_path_pcnbuild(global_pu
, pcn
, dirp
);
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
);