1 #include "fs.h" 2 3 /*===========================================================================* 4 * fs_trunc * 5 *===========================================================================*/ 6 int fs_trunc(ino_t ino_nr, off_t start, off_t end) 7 { 8 int r; 9 struct puffs_node *pn; 10 PUFFS_MAKECRED(pcr, &global_kcred); 11 12 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL) 13 return(EINVAL); 14 15 if (end == 0) { 16 struct vattr va; 17 18 if (pn->pn_va.va_size == (u_quad_t) start) 19 return(OK); 20 21 if (global_pu->pu_ops.puffs_node_setattr == NULL) 22 return(EINVAL); 23 24 puffs_vattr_null(&va); 25 va.va_size = start; 26 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; 33 34 if (global_pu->pu_ops.puffs_node_write == NULL) 35 return(EINVAL); 36 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); 42 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); 47 } 48 49 update_timens(pn, CTIME | MTIME, NULL); 50 51 return(r); 52 } 53 54 55 /*===========================================================================* 56 * fs_link * 57 *===========================================================================*/ 58 int fs_link(ino_t dir_nr, char *name, ino_t ino_nr) 59 { 60 /* Perform the link(name1, name2) system call. */ 61 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}}; 68 69 if (global_pu->pu_ops.puffs_node_link == NULL) 70 return(OK); 71 72 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL) 73 return(EINVAL); 74 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); 78 79 /* Linking directories is too dangerous to allow. */ 80 if (S_ISDIR(pn->pn_va.va_mode)) 81 return(EPERM); 82 83 if ((pn_dir = puffs_pn_nodewalk(global_pu, find_inode_cb, &dir_nr)) == NULL) 84 return(EINVAL); 85 86 if (pn_dir->pn_va.va_nlink == NO_LINK) { 87 /* Dir does not actually exist */ 88 return(ENOENT); 89 } 90 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; 97 } 98 99 if (r != OK) return(r); 100 101 /* Try to link. */ 102 pcn.pcn_namelen = strlen(name); 103 assert(pcn.pcn_namelen <= NAME_MAX); 104 strcpy(pcn.pcn_name, name); 105 106 if (buildpath) { 107 if (puffs_path_pcnbuild(global_pu, &pcn, pn_dir) != 0) 108 return(EINVAL); 109 } 110 111 if (global_pu->pu_ops.puffs_node_link(global_pu, pn_dir, pn, &pcn) != 0) 112 r = EINVAL; 113 114 if (buildpath) 115 global_pu->pu_pathfree(global_pu, &pcn.pcn_po_full); 116 117 if (r != OK) return(EINVAL); 118 119 (void)clock_time(&cur_time); 120 update_timens(pn, CTIME, &cur_time); 121 update_timens(pn_dir, MTIME | CTIME, &cur_time); 122 123 return(OK); 124 } 125 126 127 /*===========================================================================* 128 * fs_rdlink * 129 *===========================================================================*/ 130 ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes) 131 { 132 register int r; /* return value */ 133 struct puffs_node *pn; 134 char path[PATH_MAX]; 135 PUFFS_MAKECRED(pcr, &global_kcred); 136 137 if (bytes > sizeof(path)) 138 bytes = sizeof(path); 139 140 if ((pn = puffs_pn_nodewalk(global_pu, find_inode_cb, &ino_nr)) == NULL) 141 return(EINVAL); 142 143 if (!S_ISLNK(pn->pn_va.va_mode)) 144 return(EACCES); 145 146 if (global_pu->pu_ops.puffs_node_readlink == NULL) 147 return(EINVAL); 148 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); 153 } 154 155 r = fsdriver_copyout(data, 0, path, bytes); 156 157 return (r == OK) ? (ssize_t)bytes : r; 158 } 159 160 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) 166 { 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; 180 181 if (global_pu->pu_ops.puffs_node_rename == NULL) 182 return(EINVAL); 183 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); 188 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); 193 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); 198 199 old_ip = advance(old_dirp, pcn_src.pcn_name); 200 if (!old_ip) 201 return(err_code); 202 203 if (old_ip->pn_mountpoint) 204 return(EBUSY); 205 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); 214 } 215 } 216 217 /* not required to exist */ 218 new_ip = advance(new_dirp, pcn_targ.pcn_name); 219 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); 223 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; 229 } 230 231 /* Check for a variety of possible errors. */ 232 same_pdir = (old_dirp == new_dirp); 233 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); 239 } 240 } else { 241 if (old_ip == new_ip) /* old=new */ 242 return(OK); /* do NOT update directory times in this case */ 243 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); 248 } 249 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. */ 255 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. 262 */ 263 264 if (buildpath) { 265 pcn_src.pcn_po_full = old_ip->pn_po; 266 267 if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0) 268 return(EINVAL); 269 } 270 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; 274 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; 281 282 /* handle this node */ 283 po_old = old_ip->pn_po; 284 old_ip->pn_po = pcn_targ.pcn_po_full; 285 286 if (old_ip->pn_va.va_type != VDIR) { 287 global_pu->pu_pathfree(global_pu, &po_old); 288 return(OK); 289 } 290 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; 294 295 PU_LOCK(); 296 if (puffs_pn_nodewalk(global_pu, puffs_path_prefixadj, &pi) 297 != NULL) { 298 /* Actually nomem */ 299 return(EINVAL); 300 } 301 PU_UNLOCK(); 302 global_pu->pu_pathfree(global_pu, &po_old); 303 } 304 } 305 306 (void)clock_time(&cur_time); 307 update_timens(old_dirp, MTIME | CTIME, &cur_time); 308 update_timens(new_dirp, MTIME | CTIME, &cur_time); 309 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); 313 } 314 315 return(r); 316 } 317 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); 322 323 /*===========================================================================* 324 * fs_unlink * 325 *===========================================================================*/ 326 int fs_unlink(ino_t dir_nr, char *name, int call) 327 { 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. 330 */ 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); 337 338 /* Copy the last component */ 339 pcn.pcn_namelen = strlen(name); 340 assert(pcn.pcn_namelen <= NAME_MAX); 341 strcpy(pcn.pcn_name, name); 342 343 if ((pn_dir = puffs_pn_nodewalk(global_pu, find_inode_cb, &dir_nr)) == NULL) 344 return(EINVAL); 345 346 /* The last directory exists. Does the file also exist? */ 347 pn = advance(pn_dir, pcn.pcn_name); 348 r = err_code; 349 350 /* If error, return pnode. */ 351 if (r != OK) 352 return(r); 353 if (pn->pn_mountpoint) 354 return EBUSY; 355 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 */ 361 } 362 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); 367 } 368 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(). 373 */ 374 if (pn->pn_count == 0) 375 release_node(global_pu, pn); 376 377 return(r); 378 } 379 380 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 */ 388 ) 389 { 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) 396 */ 397 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; 406 407 if (global_pu->pu_ops.puffs_node_rmdir == NULL) 408 return(EINVAL); 409 410 if (!S_ISDIR(pn->pn_va.va_mode)) 411 return(ENOTDIR); 412 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); 418 419 if (pn->pn_va.va_fileid == global_pu->pu_pn_root->pn_va.va_fileid) 420 return(EBUSY); /* can't remove 'root' */ 421 422 if (buildpath) { 423 r = puffs_path_pcnbuild(global_pu, pcn, pn_dir); 424 if (r) return(EINVAL); 425 } 426 427 r = global_pu->pu_ops.puffs_node_rmdir(global_pu, pn_dir, pn, pcn); 428 429 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full); 430 431 if (r) return(EINVAL); 432 433 return(OK); 434 } 435 436 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 */ 444 ) 445 { 446 /* Unlink 'file_name'; pn must be the pnode of 'file_name' */ 447 int r; 448 449 assert(pn != NULL); 450 451 if (global_pu->pu_ops.puffs_node_remove == NULL) 452 return(EINVAL); 453 454 if (S_ISDIR(pn->pn_va.va_mode)) 455 return(EPERM); 456 457 if (buildpath) { 458 r = puffs_path_pcnbuild(global_pu, pcn, dirp); 459 if (r) 460 return(EINVAL); 461 } 462 463 r = global_pu->pu_ops.puffs_node_remove(global_pu, dirp, pn, pcn); 464 465 global_pu->pu_pathfree(global_pu, &pcn->pcn_po_full); 466 467 if (r) return(EINVAL); 468 469 return(OK); 470 } 471