├── README.md ├── .gitignore ├── oldest_file.c ├── LICENSE └── rotatefs.c /README.md: -------------------------------------------------------------------------------- 1 | # rotatefs 2 | FUSE filesystem that removes the oldest file whenever there is no space left to do some operation. 3 | 4 | ## Compile with 5 | 6 | gcc -Wall rotatefs.c `pkg-config fuse --cflags --libs` -lulockmgr -o rotatefs 7 | 8 | You must have FUSE (version 2.9) installed to compile rotatefs. 9 | 10 | ## Usage 11 | 12 | rotatefs [FUSE and mount options] rootDir mountPoint 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /oldest_file.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 600 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | char oldest_path[PATH_MAX]; 12 | time_t oldest_mtime; 13 | 14 | int files_traversed = 0; 15 | 16 | int save_older (const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) 17 | { 18 | if (typeflag != FTW_F) return 0; 19 | 20 | if (files_traversed == 0 || sb->st_mtime < oldest_mtime) { 21 | strcpy(oldest_path, fpath); 22 | oldest_mtime = sb->st_mtime; 23 | files_traversed++; 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | if (nftw(argv[1], save_older, FOPEN_MAX, FTW_MOUNT | FTW_PHYS) != 0) 32 | perror("error ocurred: "); 33 | 34 | printf("oldest file: %s\n", oldest_path); 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /rotatefs.c: -------------------------------------------------------------------------------- 1 | /** @file 2 | * 3 | * Compile with: 4 | * 5 | * gcc -Wall rotatefs.c `pkg-config fuse --cflags --libs` -lulockmgr -o rotatefs 6 | * 7 | */ 8 | 9 | #define _FILE_OFFSET_BITS 64 10 | #define FUSE_USE_VERSION 29 11 | 12 | #ifdef HAVE_CONFIG_H 13 | #include 14 | #endif 15 | 16 | #define _GNU_SOURCE 17 | #define _XOPEN_SOURCE 600 18 | 19 | #include 20 | 21 | #ifdef HAVE_LIBULOCKMGR 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #ifdef HAVE_SETXATTR 36 | #include 37 | #endif 38 | #include /* flock(2) */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | struct rfs_state { 46 | char *rootdir; 47 | char oldest_path[PATH_MAX]; 48 | int files_traversed; 49 | time_t oldest_mtime; 50 | size_t directory_usage; 51 | }; 52 | #define RFS_DATA ((struct rfs_state *) fuse_get_context()->private_data) 53 | 54 | static struct options { 55 | size_t max_device_size; 56 | } options; 57 | 58 | int save_older (const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) 59 | { 60 | 61 | if (typeflag != FTW_F) return 0; 62 | 63 | if (RFS_DATA->files_traversed == 0 || sb->st_mtime < RFS_DATA->oldest_mtime) { 64 | strcpy(RFS_DATA->oldest_path, fpath); 65 | RFS_DATA->oldest_mtime = sb->st_mtime; 66 | RFS_DATA->files_traversed++; 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | int rm_empty_dirs(const char *fpath) 73 | { 74 | char path_copy[PATH_MAX]; 75 | char *dirpath; 76 | int res = 0; 77 | 78 | strcpy(path_copy, fpath); 79 | dirpath = path_copy; 80 | 81 | do { 82 | dirpath = dirname(dirpath); 83 | 84 | /* dirname("") == "." (error) 85 | * dirname("/") == "/" (ok) 86 | * dirname(".") == "." (error) 87 | * dirname("..") == "." (error) 88 | * dirname("a/b") == "a" (error) 89 | * So, assuming the path received by this function always start with a 90 | * '/', because it expects a full path. If the dirname have a lenght of 91 | * 1 there is no more directories to remove, or an invalid path was 92 | * given. 93 | */ 94 | switch (strnlen(dirpath, 2)) { 95 | case 0: 96 | errno = EINVAL; 97 | return -errno; 98 | case 1: 99 | if (dirpath[0] == '/') 100 | return 0; 101 | else { 102 | errno = EINVAL; 103 | return -errno; 104 | } 105 | } 106 | res = rmdir(dirpath); 107 | } while(res == 0); 108 | 109 | return (errno == ENOTEMPTY) ? 0 : -errno; 110 | } 111 | 112 | int delete_oldest() 113 | { 114 | int res; 115 | 116 | if (nftw(RFS_DATA->rootdir, save_older, FOPEN_MAX, FTW_MOUNT | FTW_PHYS) != 0) { 117 | perror("error ocurred: "); 118 | return -errno; 119 | } 120 | 121 | res = unlink(RFS_DATA->oldest_path); 122 | if (res == -1) 123 | return -errno; 124 | 125 | /* after the file deleted, will need to search for the oldest file again. */ 126 | RFS_DATA->files_traversed = 0; 127 | 128 | return rm_empty_dirs(RFS_DATA->oldest_path); 129 | } 130 | 131 | int sum(const char *fpath, const struct stat *sb, int typeflag) { 132 | RFS_DATA->directory_usage += sb->st_size; 133 | return 0; 134 | } 135 | 136 | size_t current_size(char *rootdir) 137 | { 138 | RFS_DATA->directory_usage = 0; 139 | 140 | if (ftw(rootdir, &sum, 1)) 141 | return -errno; 142 | else 143 | return RFS_DATA->directory_usage; 144 | } 145 | 146 | size_t device_size(char *rootdir) 147 | { 148 | int res; 149 | size_t fsize; 150 | size_t max_device_size = options.max_device_size; 151 | struct statvfs *stbuf = malloc(sizeof(struct statvfs)); 152 | 153 | res = statvfs(rootdir, stbuf); 154 | fsize = stbuf->f_bsize * stbuf->f_blocks; 155 | free(stbuf); 156 | if (res == -1) { 157 | return -errno; 158 | } 159 | 160 | if (max_device_size > 0) 161 | return (fsize < max_device_size ? fsize : max_device_size); 162 | else 163 | return fsize; 164 | } 165 | 166 | void trim_fs(size_t size) 167 | { 168 | if (device_size(RFS_DATA->rootdir) < size) 169 | return; 170 | 171 | while (device_size(RFS_DATA->rootdir) < (current_size(RFS_DATA->rootdir) + size)) { 172 | fprintf(stderr, "device_size: %ld; current_size: %ld\n", device_size(RFS_DATA->rootdir), current_size(RFS_DATA->rootdir)); 173 | if (delete_oldest() != 0) { 174 | return; 175 | } 176 | } 177 | } 178 | 179 | // All the paths I see are relative to the root of the mounted 180 | // filesystem. In order to get to the underlying filesystem, I need to 181 | // have the mountpoint. I'll save it away early on in main(), and then 182 | // whenever I need a path for something I'll call this to construct 183 | // it. 184 | static void fullpath(char fpath[PATH_MAX], const char *path) 185 | { 186 | strcpy(fpath, RFS_DATA->rootdir); 187 | strncat(fpath, path, PATH_MAX); // ridiculously long paths will 188 | // break here 189 | } 190 | 191 | static void *rfs_init(struct fuse_conn_info *conn) 192 | { 193 | (void) conn; 194 | 195 | // trim filesystem to the max allowed size on initialization 196 | trim_fs(0); 197 | 198 | return RFS_DATA; 199 | } 200 | 201 | static int rfs_getattr(const char *path, struct stat *stbuf) 202 | { 203 | int res; 204 | char fpath[PATH_MAX]; 205 | 206 | fullpath(fpath, path); 207 | res = lstat(fpath, stbuf); 208 | if (res == -1) 209 | return -errno; 210 | 211 | return 0; 212 | } 213 | 214 | static int rfs_fgetattr(const char *path, struct stat *stbuf, 215 | struct fuse_file_info *fi) 216 | { 217 | int res; 218 | 219 | (void) path; 220 | 221 | res = fstat(fi->fh, stbuf); 222 | if (res == -1) 223 | return -errno; 224 | 225 | return 0; 226 | } 227 | 228 | static int rfs_access(const char *path, int mask) 229 | { 230 | int res; 231 | char fpath[PATH_MAX]; 232 | 233 | fullpath(fpath, path); 234 | res = access(fpath, mask); 235 | if (res == -1) 236 | return -errno; 237 | 238 | return 0; 239 | } 240 | 241 | static int rfs_readlink(const char *path, char *buf, size_t size) 242 | { 243 | int res; 244 | char fpath[PATH_MAX]; 245 | 246 | fullpath(fpath, path); 247 | res = readlink(fpath, buf, size - 1); 248 | if (res == -1) 249 | return -errno; 250 | 251 | buf[res] = '\0'; 252 | return 0; 253 | } 254 | 255 | struct rfs_dirp { 256 | DIR *dp; 257 | struct dirent *entry; 258 | off_t offset; 259 | }; 260 | 261 | static int rfs_opendir(const char *path, struct fuse_file_info *fi) 262 | { 263 | int res; 264 | char fpath[PATH_MAX]; 265 | 266 | fullpath(fpath, path); 267 | struct rfs_dirp *d = malloc(sizeof(struct rfs_dirp)); 268 | if (d == NULL) 269 | return -ENOMEM; 270 | 271 | d->dp = opendir(fpath); 272 | if (d->dp == NULL) { 273 | res = -errno; 274 | free(d); 275 | return res; 276 | } 277 | d->offset = 0; 278 | d->entry = NULL; 279 | 280 | fi->fh = (unsigned long) d; 281 | return 0; 282 | } 283 | 284 | static inline struct rfs_dirp *get_dirp(struct fuse_file_info *fi) 285 | { 286 | return (struct rfs_dirp *) (uintptr_t) fi->fh; 287 | } 288 | 289 | static int rfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 290 | off_t offset, struct fuse_file_info *fi) 291 | { 292 | struct rfs_dirp *d = get_dirp(fi); 293 | 294 | (void) path; 295 | if (offset != d->offset) { 296 | seekdir(d->dp, offset); 297 | d->entry = NULL; 298 | d->offset = offset; 299 | } 300 | while (1) { 301 | struct stat st; 302 | off_t nextoff; 303 | 304 | if (!d->entry) { 305 | d->entry = readdir(d->dp); 306 | if (!d->entry) 307 | break; 308 | } 309 | 310 | memset(&st, 0, sizeof(st)); 311 | st.st_ino = d->entry->d_ino; 312 | st.st_mode = d->entry->d_type << 12; 313 | nextoff = telldir(d->dp); 314 | if (filler(buf, d->entry->d_name, &st, nextoff)) 315 | break; 316 | 317 | d->entry = NULL; 318 | d->offset = nextoff; 319 | } 320 | 321 | return 0; 322 | } 323 | 324 | static int rfs_releasedir(const char *path, struct fuse_file_info *fi) 325 | { 326 | struct rfs_dirp *d = get_dirp(fi); 327 | (void) path; 328 | closedir(d->dp); 329 | free(d); 330 | return 0; 331 | } 332 | 333 | static int rfs_mknod(const char *path, mode_t mode, dev_t rdev) 334 | { 335 | int res; 336 | char fpath[PATH_MAX]; 337 | 338 | fullpath(fpath, path); 339 | if (S_ISFIFO(mode)) 340 | res = mkfifo(fpath, mode); 341 | else 342 | res = mknod(fpath, mode, rdev); 343 | if (res == -1) 344 | return -errno; 345 | 346 | return 0; 347 | } 348 | 349 | static int rfs_mkdir(const char *path, mode_t mode) 350 | { 351 | int res; 352 | char fpath[PATH_MAX]; 353 | 354 | fullpath(fpath, path); 355 | res = mkdir(fpath, mode); 356 | if (res == -1) 357 | return -errno; 358 | 359 | return 0; 360 | } 361 | 362 | static int rfs_unlink(const char *path) 363 | { 364 | int res; 365 | char fpath[PATH_MAX]; 366 | 367 | fullpath(fpath, path); 368 | res = unlink(fpath); 369 | if (res == -1) 370 | return -errno; 371 | 372 | return 0; 373 | } 374 | 375 | static int rfs_rmdir(const char *path) 376 | { 377 | int res; 378 | char fpath[PATH_MAX]; 379 | 380 | fullpath(fpath, path); 381 | res = rmdir(fpath); 382 | if (res == -1) 383 | return -errno; 384 | 385 | return 0; 386 | } 387 | 388 | static int rfs_symlink(const char *from, const char *to) 389 | { 390 | int res; 391 | char ffrom[PATH_MAX]; 392 | char fto[PATH_MAX]; 393 | 394 | fullpath(ffrom, from); 395 | fullpath(fto, to); 396 | res = symlink(ffrom, fto); 397 | if (res == -1) 398 | return -errno; 399 | 400 | return 0; 401 | } 402 | 403 | static int rfs_rename(const char *from, const char *to) 404 | { 405 | int res; 406 | char ffrom[PATH_MAX]; 407 | char fto[PATH_MAX]; 408 | 409 | fullpath(ffrom, from); 410 | fullpath(fto, to); 411 | res = rename(ffrom, fto); 412 | if (res == -1) 413 | return -errno; 414 | 415 | return 0; 416 | } 417 | 418 | static int rfs_link(const char *from, const char *to) 419 | { 420 | int res; 421 | char ffrom[PATH_MAX]; 422 | char fto[PATH_MAX]; 423 | 424 | fullpath(ffrom, from); 425 | fullpath(fto, to); 426 | res = link(ffrom, fto); 427 | if (res == -1) 428 | return -errno; 429 | 430 | return 0; 431 | } 432 | 433 | static int rfs_chmod(const char *path, mode_t mode) 434 | { 435 | int res; 436 | char fpath[PATH_MAX]; 437 | 438 | fullpath(fpath, path); 439 | res = chmod(fpath, mode); 440 | if (res == -1) 441 | return -errno; 442 | 443 | return 0; 444 | } 445 | 446 | static int rfs_chown(const char *path, uid_t uid, gid_t gid) 447 | { 448 | int res; 449 | char fpath[PATH_MAX]; 450 | 451 | fullpath(fpath, path); 452 | res = lchown(fpath, uid, gid); 453 | if (res == -1) 454 | return -errno; 455 | 456 | return 0; 457 | } 458 | 459 | static int rfs_truncate(const char *path, off_t size) 460 | { 461 | int res; 462 | char fpath[PATH_MAX]; 463 | 464 | fullpath(fpath, path); 465 | res = truncate(fpath, size); 466 | if (res == -1) 467 | return -errno; 468 | 469 | return 0; 470 | } 471 | 472 | static int rfs_ftruncate(const char *path, off_t size, 473 | struct fuse_file_info *fi) 474 | { 475 | int res; 476 | 477 | (void) path; 478 | 479 | res = ftruncate(fi->fh, size); 480 | if (res == -1) 481 | return -errno; 482 | 483 | return 0; 484 | } 485 | 486 | #ifdef HAVE_UTIMENSAT 487 | static int rfs_utimens(const char *path, const struct timespec ts[2], 488 | struct fuse_file_info *fi) 489 | { 490 | int res; 491 | char fpath[PATH_MAX]; 492 | 493 | /* don't use utime/utimes since they follow symlinks */ 494 | if (fi) 495 | res = futimens(fi->fh, ts); 496 | else { 497 | fullpath(fpath, path); 498 | res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); 499 | } 500 | if (res == -1) 501 | return -errno; 502 | 503 | return 0; 504 | } 505 | #endif 506 | 507 | static int rfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) 508 | { 509 | int fd; 510 | char fpath[PATH_MAX]; 511 | 512 | fullpath(fpath, path); 513 | fd = open(fpath, fi->flags, mode); 514 | if (fd == -1) 515 | return -errno; 516 | 517 | fi->fh = fd; 518 | return 0; 519 | } 520 | 521 | static int rfs_open(const char *path, struct fuse_file_info *fi) 522 | { 523 | int fd; 524 | char fpath[PATH_MAX]; 525 | 526 | fullpath(fpath, path); 527 | fd = open(fpath, fi->flags); 528 | if (fd == -1) 529 | return -errno; 530 | 531 | fi->fh = fd; 532 | return 0; 533 | } 534 | 535 | static int rfs_read(const char *path, char *buf, size_t size, off_t offset, 536 | struct fuse_file_info *fi) 537 | { 538 | int res; 539 | 540 | (void) path; 541 | res = pread(fi->fh, buf, size, offset); 542 | if (res == -1) 543 | res = -errno; 544 | 545 | return res; 546 | } 547 | 548 | static int rfs_read_buf(const char *path, struct fuse_bufvec **bufp, 549 | size_t size, off_t offset, struct fuse_file_info *fi) 550 | { 551 | struct fuse_bufvec *src; 552 | 553 | (void) path; 554 | 555 | src = malloc(sizeof(struct fuse_bufvec)); 556 | if (src == NULL) 557 | return -ENOMEM; 558 | 559 | *src = FUSE_BUFVEC_INIT(size); 560 | 561 | src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; 562 | src->buf[0].fd = fi->fh; 563 | src->buf[0].pos = offset; 564 | 565 | *bufp = src; 566 | 567 | return 0; 568 | } 569 | 570 | static int rfs_write(const char *path, const char *buf, size_t size, 571 | off_t offset, struct fuse_file_info *fi) 572 | { 573 | int res; 574 | 575 | (void) path; 576 | 577 | trim_fs(size); 578 | for (res = pwrite(fi->fh, buf, size, offset); res == -1 && errno == ENOSPC; res = pwrite(fi->fh, buf, size, offset)) { 579 | fprintf(stderr, "device_size: %ld; size: %ld\n", device_size(RFS_DATA->rootdir), size); 580 | if (device_size(RFS_DATA->rootdir) < size || delete_oldest(RFS_DATA->rootdir) != 0) { 581 | break; 582 | } 583 | } 584 | 585 | if (res == -1) 586 | res = -errno; 587 | 588 | return res; 589 | } 590 | 591 | static int rfs_write_buf(const char *path, struct fuse_bufvec *buf, 592 | off_t offset, struct fuse_file_info *fi) 593 | { 594 | struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf)); 595 | int res; 596 | 597 | (void) path; 598 | 599 | dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; 600 | dst.buf[0].fd = fi->fh; 601 | dst.buf[0].pos = offset; 602 | 603 | trim_fs(fuse_buf_size(buf)); 604 | for (res = fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK); res == -ENOSPC; res = fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK)) { 605 | fprintf(stderr, "device_size: %ld; fuse_buf_size(buf): %ld\n", device_size(RFS_DATA->rootdir), fuse_buf_size(buf)); 606 | if (device_size(RFS_DATA->rootdir) < fuse_buf_size(buf) || delete_oldest() != 0) { 607 | break; 608 | } 609 | } 610 | 611 | return res; 612 | } 613 | 614 | static int rfs_statfs(const char *path, struct statvfs *stbuf) 615 | { 616 | int res; 617 | char fpath[PATH_MAX]; 618 | 619 | fullpath(fpath, path); 620 | res = statvfs(fpath, stbuf); 621 | if (res == -1) 622 | return -errno; 623 | 624 | return 0; 625 | } 626 | 627 | static int rfs_flush(const char *path, struct fuse_file_info *fi) 628 | { 629 | int res; 630 | 631 | (void) path; 632 | /* This is called from every close on an open file, so call the 633 | close on the underlying filesystem. But since flush may be 634 | called multiple times for an open file, this must not really 635 | close the file. This is important if used on a network 636 | filesystem like NFS which flush the data/metadata on close() */ 637 | res = close(dup(fi->fh)); 638 | if (res == -1) 639 | return -errno; 640 | 641 | return 0; 642 | } 643 | 644 | static int rfs_release(const char *path, struct fuse_file_info *fi) 645 | { 646 | (void) path; 647 | close(fi->fh); 648 | 649 | return 0; 650 | } 651 | 652 | static int rfs_fsync(const char *path, int isdatasync, 653 | struct fuse_file_info *fi) 654 | { 655 | int res; 656 | (void) path; 657 | 658 | #ifndef HAVE_FDATASYNC 659 | (void) isdatasync; 660 | #else 661 | if (isdatasync) 662 | res = fdatasync(fi->fh); 663 | else 664 | #endif 665 | res = fsync(fi->fh); 666 | if (res == -1) 667 | return -errno; 668 | 669 | return 0; 670 | } 671 | 672 | #ifdef HAVE_POSIX_FALLOCATE 673 | static int rfs_fallocate(const char *path, int mode, 674 | off_t offset, off_t length, struct fuse_file_info *fi) 675 | { 676 | (void) path; 677 | 678 | if (mode) 679 | return -EOPNOTSUPP; 680 | 681 | return -posix_fallocate(fi->fh, offset, length); 682 | } 683 | #endif 684 | 685 | #ifdef HAVE_SETXATTR 686 | /* xattr operations are optional and can safely be left unimplemented */ 687 | static int rfs_setxattr(const char *path, const char *name, const char *value, 688 | size_t size, int flags) 689 | { 690 | char fpath[PATH_MAX]; 691 | 692 | fullpath(fpath, path); 693 | int res = lsetxattr(fpath, name, value, size, flags); 694 | if (res == -1) 695 | return -errno; 696 | return 0; 697 | } 698 | 699 | static int rfs_getxattr(const char *path, const char *name, char *value, 700 | size_t size) 701 | { 702 | char fpath[PATH_MAX]; 703 | 704 | fullpath(fpath, path); 705 | int res = lgetxattr(fpath, name, value, size); 706 | if (res == -1) 707 | return -errno; 708 | return res; 709 | } 710 | 711 | static int rfs_listxattr(const char *path, char *list, size_t size) 712 | { 713 | char fpath[PATH_MAX]; 714 | 715 | fullpath(fpath, path); 716 | int res = llistxattr(fpath, list, size); 717 | if (res == -1) 718 | return -errno; 719 | return res; 720 | } 721 | 722 | static int rfs_removexattr(const char *path, const char *name) 723 | { 724 | char fpath[PATH_MAX]; 725 | 726 | fullpath(fpath, path); 727 | int res = lremovexattr(fpath, name); 728 | if (res == -1) 729 | return -errno; 730 | return 0; 731 | } 732 | #endif /* HAVE_SETXATTR */ 733 | 734 | #ifdef HAVE_LIBULOCKMGR 735 | static int rfs_lock(const char *path, struct fuse_file_info *fi, int cmd, 736 | struct flock *lock) 737 | { 738 | (void) path; 739 | 740 | return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner, 741 | sizeof(fi->lock_owner)); 742 | } 743 | #endif 744 | 745 | static int rfs_flock(const char *path, struct fuse_file_info *fi, int op) 746 | { 747 | int res; 748 | (void) path; 749 | 750 | res = flock(fi->fh, op); 751 | if (res == -1) 752 | return -errno; 753 | 754 | return 0; 755 | } 756 | 757 | static struct fuse_operations rfs_oper = { 758 | .init = rfs_init, 759 | .getattr = rfs_getattr, 760 | .fgetattr = rfs_fgetattr, 761 | .access = rfs_access, 762 | .readlink = rfs_readlink, 763 | .opendir = rfs_opendir, 764 | .readdir = rfs_readdir, 765 | .releasedir = rfs_releasedir, 766 | .mknod = rfs_mknod, 767 | .mkdir = rfs_mkdir, 768 | .symlink = rfs_symlink, 769 | .unlink = rfs_unlink, 770 | .rmdir = rfs_rmdir, 771 | .rename = rfs_rename, 772 | .link = rfs_link, 773 | .chmod = rfs_chmod, 774 | .chown = rfs_chown, 775 | .truncate = rfs_truncate, 776 | .ftruncate = rfs_ftruncate, 777 | #ifdef HAVE_UTIMENSAT 778 | .utimens = rfs_utimens, 779 | #endif 780 | .create = rfs_create, 781 | .open = rfs_open, 782 | .read = rfs_read, 783 | .read_buf = rfs_read_buf, 784 | .write = rfs_write, 785 | .write_buf = rfs_write_buf, 786 | .statfs = rfs_statfs, 787 | .flush = rfs_flush, 788 | .release = rfs_release, 789 | .fsync = rfs_fsync, 790 | #ifdef HAVE_POSIX_FALLOCATE 791 | .fallocate = rfs_fallocate, 792 | #endif 793 | #ifdef HAVE_SETXATTR 794 | .setxattr = rfs_setxattr, 795 | .getxattr = rfs_getxattr, 796 | .listxattr = rfs_listxattr, 797 | .removexattr = rfs_removexattr, 798 | #endif 799 | #ifdef HAVE_LIBULOCKMGR 800 | .lock = rfs_lock, 801 | #endif 802 | .flock = rfs_flock, 803 | }; 804 | 805 | void rfs_usage() 806 | { 807 | fprintf(stderr, "usage: rotatefs [FUSE and mount options] rootDir mountPoint [-s |--size=]\n"); 808 | abort(); 809 | } 810 | 811 | #define OPTION(t, p) { t, offsetof(struct options, p), 0 } 812 | static const struct fuse_opt option_spec[] = { 813 | OPTION("--size=%zu", max_device_size), 814 | OPTION("-s %zu", max_device_size), 815 | FUSE_OPT_END 816 | }; 817 | 818 | int main(int argc, char *argv[]) 819 | { 820 | int fuse_stat, i; 821 | struct rfs_state *rfs_data; 822 | 823 | umask(0); 824 | 825 | // See which version of fuse we're running 826 | fprintf(stderr, "Fuse library version %d.%d\n", FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION); 827 | 828 | // Perform some sanity checking on the command line: make sure 829 | // there are enough arguments, and that neither of the last two 830 | // start with a hyphen (this will break if you actually have a 831 | // rootpoint or mountpoint whose name starts with a hyphen, but so 832 | // will a zillion other programs) 833 | if ((argc < 3)) { 834 | for (i = 0; i < argc; i++) 835 | fprintf(stderr, "argv[%d]: %s\n", i, argv[i]); 836 | rfs_usage(); 837 | } 838 | 839 | rfs_data = malloc(sizeof(struct rfs_state)); 840 | if (rfs_data == NULL) { 841 | perror("main calloc"); 842 | abort(); 843 | } 844 | // Pull the rootdir out of the argument list and save it in my 845 | // internal data 846 | rfs_data->rootdir = realpath(argv[1], NULL); 847 | for (i = 2; i < argc; i++) { 848 | argv[i-1] = argv[i]; 849 | } 850 | argv[argc-1] = NULL; 851 | argc--; 852 | fprintf(stderr, "rootdir: %s\n", rfs_data->rootdir); 853 | 854 | options.max_device_size = 0; // default value 855 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 856 | if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) 857 | rfs_usage(); 858 | fprintf(stderr, "options.max_device_size: %lu\n", options.max_device_size); 859 | fprintf(stderr, "device_size(): %lu\n", device_size(rfs_data->rootdir)); 860 | //fprintf(stderr, "current_size(): %lu\n", current_size(rfs_data->rootdir)); 861 | 862 | rfs_data->files_traversed = 0; 863 | 864 | // turn over control to fuse 865 | fuse_stat = fuse_main(args.argc, args.argv, &rfs_oper, rfs_data); 866 | 867 | return fuse_stat; 868 | } 869 | --------------------------------------------------------------------------------