/* * Reproducer for something like this * * [data][prealloc][data] * * and then in the [prealloc] section we have * * [ pre ][pre][ pre ] * [d][pp][dd][ppp][d][ppp][d] * * where each letter represents on block of either data or prealloc. * * This explains all the weirdly specific numbers. */ static int test_six() { int character = (random() % 126) + 33; int i; memset(buf, character, 4096); /* Write on either side of the file, leaving a hole in the middle */ for (i = 0; i < 10; i++) { if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } /* * The test fs I had the prealloc extent was 13 4k blocks long so I'm * just using that to give myself the best chances of reproducing. */ for (i = 23; i < 33; i++) { if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } if (fallocate(test_fd, 0, 10 * 4096, 4 * 4096)) { fprintf(stderr, "Error fallocating %d\n", errno); return 1; } if (fallocate(test_fd, 0, 14 * 4096, 5 * 4096)) { fprintf(stderr, "Error fallocating %d\n", errno); return 1; } if (fallocate(test_fd, 0, 19 * 4096, 4 * 4096)) { fprintf(stderr, "Error fallocating %d\n", errno); return 1; } if (pwrite(test_fd, buf, 4096, 10 * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } for (i = 13; i < 15; i++) { if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } if (pwrite(test_fd, buf, 4096, 18 * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } if (pwrite(test_fd, buf, 4096, 22 * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } if (fsync(test_fd)) { fprintf(stderr, "Fsync failed %d\n", errno); return 1; } return 0; }
/* * Randomly write inside of a file, either creating a sparse file or prealloc * the file and randomly write within it, depending on the prealloc flag */ static int test_three(int *max_blocks, int prealloc, int rand_fsync, int do_sync, int drop_caches) { int size = (random() % 2048) + 4; int blocks = size / 2; int sync_block = blocks / 2; int rand_sync_interval = (random() % blocks) + 1; int character = (random() % 126) + 33; if (prealloc && fallocate(test_fd, 0, 0, size * 4096)) { fprintf(stderr, "Error fallocating %d (%s)\n", errno, strerror(errno)); return 1; } if (prealloc) *max_blocks = size; memset(buf, character, 4096); while (blocks--) { int block = (random() % size); if ((block + 1) > *max_blocks) *max_blocks = block + 1; if (rand_fsync && !(blocks % rand_sync_interval)) { if (fsync(test_fd)) { fprintf(stderr, "Fsync failed, test results " "will be invalid: %d\n", errno); return 1; } } /* Force a transaction commit in between just for fun */ if (blocks == sync_block && (do_sync || drop_caches)) { if (do_sync) sync(); else sync_file_range(test_fd, 0, 0, SYNC_FILE_RANGE_WRITE| SYNC_FILE_RANGE_WAIT_AFTER); if (drop_caches) { close(test_fd); drop_all_caches(); test_fd = open(fname, O_RDWR); if (test_fd < 0) { test_fd = 0; fprintf(stderr, "Error re-opening file: %d\n", errno); return 1; } } } if (pwrite(test_fd, buf, 4096, block * 4096) < 4096) { fprintf(stderr, "Short write %d\n", errno); return 1; } } return 0; }
/* * Leaves f->fd open on success, caller must close */ static int extend_file(struct thread_data *td, struct fio_file *f) { int r, new_layout = 0, unlink_file = 0, flags; unsigned long long left; unsigned int bs; char *b; if (read_only) { log_err("fio: refusing extend of file due to read-only\n"); return 0; } /* * check if we need to lay the file out complete again. fio * does that for operations involving reads, or for writes * where overwrite is set */ if (td_read(td) || (td_write(td) && td->o.overwrite) || (td_write(td) && td->io_ops->flags & FIO_NOEXTEND)) new_layout = 1; if (td_write(td) && !td->o.overwrite) unlink_file = 1; if (unlink_file || new_layout) { dprint(FD_FILE, "layout unlink %s\n", f->file_name); if ((unlink(f->file_name) < 0) && (errno != ENOENT)) { td_verror(td, errno, "unlink"); return 1; } } flags = O_WRONLY | O_CREAT; if (new_layout) flags |= O_TRUNC; dprint(FD_FILE, "open file %s, flags %x\n", f->file_name, flags); f->fd = open(f->file_name, flags, 0644); if (f->fd < 0) { td_verror(td, errno, "open"); return 1; } #ifdef CONFIG_POSIX_FALLOCATE if (!td->o.fill_device) { switch (td->o.fallocate_mode) { case FIO_FALLOCATE_NONE: break; case FIO_FALLOCATE_POSIX: dprint(FD_FILE, "posix_fallocate file %s size %llu\n", f->file_name, (unsigned long long) f->real_file_size); r = posix_fallocate(f->fd, 0, f->real_file_size); if (r > 0) { log_err("fio: posix_fallocate fails: %s\n", strerror(r)); } break; #ifdef CONFIG_LINUX_FALLOCATE case FIO_FALLOCATE_KEEP_SIZE: dprint(FD_FILE, "fallocate(FALLOC_FL_KEEP_SIZE) " "file %s size %llu\n", f->file_name, (unsigned long long) f->real_file_size); r = fallocate(f->fd, FALLOC_FL_KEEP_SIZE, 0, f->real_file_size); if (r != 0) td_verror(td, errno, "fallocate"); break; #endif /* CONFIG_LINUX_FALLOCATE */ default: log_err("fio: unknown fallocate mode: %d\n", td->o.fallocate_mode); assert(0); } } #endif /* CONFIG_POSIX_FALLOCATE */ if (!new_layout) goto done; /* * The size will be -1ULL when fill_device is used, so don't truncate * or fallocate this file, just write it */ if (!td->o.fill_device) { dprint(FD_FILE, "truncate file %s, size %llu\n", f->file_name, (unsigned long long) f->real_file_size); if (ftruncate(f->fd, f->real_file_size) == -1) { td_verror(td, errno, "ftruncate"); goto err; } } b = malloc(td->o.max_bs[DDIR_WRITE]); left = f->real_file_size; while (left && !td->terminate) { bs = td->o.max_bs[DDIR_WRITE]; if (bs > left) bs = left; fill_io_buffer(td, b, bs, bs); r = write(f->fd, b, bs); if (r > 0) { left -= r; continue; } else { if (r < 0) { int __e = errno; if (__e == ENOSPC) { if (td->o.fill_device) break; log_info("fio: ENOSPC on laying out " "file, stopping\n"); break; } td_verror(td, errno, "write"); } else td_verror(td, EIO, "write"); break; } } if (td->terminate) { dprint(FD_FILE, "terminate unlink %s\n", f->file_name); unlink(f->file_name); } else if (td->o.create_fsync) { if (fsync(f->fd) < 0) { td_verror(td, errno, "fsync"); goto err; } } if (td->o.fill_device && !td_write(td)) { fio_file_clear_size_known(f); if (td_io_get_file_size(td, f)) goto err; if (f->io_size > f->real_file_size) f->io_size = f->real_file_size; } free(b); done: return 0; err: close(f->fd); f->fd = -1; return 1; }
int common_open_file(int *fd, int flags, char *filename, loff_t fallosize) { struct stat statinfo; int err = 0; err = stat(filename, &statinfo); if (err < 0) { if (errno == ENOENT) { //We're reading the file /* Goddang what's the big idea setting O_RDONLY to 00 */ if (!(flags & (O_WRONLY | O_RDWR))) { perror("File open"); E("File %s not found, eventhought we wan't to read", filename); return EACCES; } else { D("COMMON_WRT: File doesn't exist. Creating it"); flags |= O_CREAT; err = 0; } } /* Directory doesn't exist */ else if (errno == ENOTDIR) { E("The directory doesn't exist"); return ENOTDIR; } else { fprintf(stderr, "Error: %s on %s\n", strerror(errno), filename); return err; } } //This will overwrite existing file.TODO: Check what is the desired default behaviour D("COMMON_WRT: Opening file %s", filename); *fd = open(filename, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (*fd < 0) { if (flags & O_WRONLY) { /* Silent version since, acquire stumbles here and it */ /* Works as planned. Could do string processing */ /* to check the dir etc. but meh */ D("File opening failed with write on for %s. Acquire should create dir", filename); return *fd; } fprintf(stderr, "Error: %s on %s\n", strerror(errno), filename); return *fd; } D(" File opened as fd %d", *fd); if (fallosize > 0) { err = fallocate(*fd, 0, 0, fallosize); if (err < 0) { perror("fallocate"); if (errno == EOPNOTSUPP) { fprintf(stdout, "COMMON_WRT: Not fallocating, since not supported\n"); err = 0; } else { fprintf(stderr, "COMMON_WRT: Fallocate failed on %s\n", filename); return err; } } D("File preallocated"); } else D("Not fallocating"); return err; }
/* size_t blen, size_t niter, size_t blen_incr, size_t nincr) */ static int run_odsync (const char* fname, const struct test_spec* sp) { int fd = -1, err = 0, rc = 0; size_t len = 0, ni = 0, nfalloc = 0, j = 0; double sec = 0.0; int oflags = O_CREAT | O_RDWR; char *buf = NULL; struct timeval tstart, tend; ssize_t n = -1; pthread_t sync_thr = (pthread_t)0; wfunc_t write_data = NULL; assert (fname && sp); srand (time(NULL)); write_data = (sp->num_chunks > 0) ? writev_nbuf : write_nbuf; oflags |= sp->open_flags; fd = open (fname, oflags, S_IRUSR|S_IWUSR); if (fd < 0) { err = errno; fprintf (stderr, "open [%s]: %s\n", fname, strerror(err)); return err; } (void) printf ("BEGIN test: buf[%ld], niter=%ld, incr=%ld, nincr=%ld, " "num_chunks=[%ld] mode=0x%x\n", (long)sp->len, (long)sp->niter, (long)sp->incr, (long)sp->nincr, (long)sp->num_chunks, sp->mode_flags); (void) printf ("opened %s with flags=0x%04x\n", fname, oflags); if (sp->mode_flags & FALLOC) { nfalloc = (sp->len * sp->niter) + (sp->incr * (sp->nincr-1)); #ifdef __linux__ rc = fallocate (fd, sp->mode_flags & FALLOC_SZ ? FALLOC_FL_KEEP_SIZE : 0, 0, nfalloc); #elif !defined (NO_FALLOCATE) rc = posix_fallocate (fd, 0, nfalloc); #endif if (rc) { err = errno; (void) fprintf (stderr, "fallocate [%ld bytes] for %s failed: %s\n", (long)nfalloc, fname, strerror (err)); } else { (void) printf ("fallocate [%ld] for [%s]\n", (long)nfalloc, fname); } } if ((sp->mode_flags & SYNC_THREAD) && (sp->mode_flags & SYNC_TYPE)) { rc = start_sync_thread (&sync_thr); } for (len = sp->len, ni = 0; !rc && (len < MAX_BUFSZ) && (ni < sp->nincr); len += sp->incr, ++ni) { if (NULL == (buf = malloc (len))) { rc = ENOMEM; break; } for (j = 0; j < len; buf[j++] = rand () & 0xFF); (void) printf (" => buf[%ld bytes]: ", (long)len); gettimeofday (&tstart, 0); n = write_data (fd, buf, len, sp); if (n <= 0) { rc = (int)n; break; } gettimeofday (&tend, 0); sec = get_seconds (&tstart, &tend); (void) printf ("%ld/%ld bytes/writes in %.6f sec, %.6f w/sec, %.6f Kb/sec\n", (long)n, (long)sp->niter, sec, (double)sp->niter/sec, ((double)n/sec)/1024); free (buf); buf = NULL; } if (buf) free (buf); (void) close (fd); if (sync_thr) { stop_sync_thread (&sync_thr); } (void) printf ("\nEND test, rc = %d\n", rc); return rc; }
int main(int argc, char *argv[]) { struct stat s; struct statfs sf; off_t offset; int fd; blksize_t blksz; off_t sz; int mode; int error; int c; int size = 1; /* punch $SIZE blocks ... */ int interval = 2; /* every $INTERVAL blocks */ while ((c = getopt(argc, argv, "i:s:")) != EOF) { switch (c) { case 'i': interval = atoi(optarg); break; case 's': size = atoi(optarg); break; default: usage(argv[0]); } } if (interval <= 0) { printf("interval must be > 0\n"); usage(argv[0]); } if (size <= 0) { printf("size must be > 0\n"); usage(argv[0]); } if (optind != argc - 1) usage(argv[0]); fd = open(argv[optind], O_WRONLY); if (fd < 0) goto err; error = fstat(fd, &s); if (error) goto err; error = fstatfs(fd, &sf); if (error) goto err; sz = s.st_size; blksz = sf.f_bsize; mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; for (offset = 0; offset < sz; offset += blksz * interval) { error = fallocate(fd, mode, offset, blksz * size); if (error) goto err; } error = fsync(fd); if (error) goto err; error = close(fd); if (error) goto err; return 0; err: perror(argv[optind]); return 2; }
static void mfd_assert_write(int fd) { ssize_t l; void *p; int r; /* verify write() succeeds */ l = write(fd, "\0\0\0\0", 4); if (l != 4) { printf("write() failed: %m\n"); abort(); } /* verify PROT_READ | PROT_WRITE is allowed */ p = mmap(NULL, MFD_DEF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, MFD_DEF_SIZE); /* verify PROT_WRITE is allowed */ p = mmap(NULL, MFD_DEF_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, MFD_DEF_SIZE); /* verify PROT_READ with MAP_SHARED is allowed and a following * mprotect(PROT_WRITE) allows writing */ p = mmap(NULL, MFD_DEF_SIZE, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); if (r < 0) { printf("mprotect() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, MFD_DEF_SIZE); /* verify PUNCH_HOLE works */ r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, MFD_DEF_SIZE); if (r < 0) { printf("fallocate(PUNCH_HOLE) failed: %m\n"); abort(); } }
int main(int argc, char **argv) { char *fname; int c; int error; int fd; int mode = 0; loff_t length = -2LL; loff_t offset = 0; struct option longopts[] = { { "help", 0, 0, 'h' }, { "keep-size", 0, 0, 'n' }, { "offset", 1, 0, 'o' }, { "length", 1, 0, 'l' }, { NULL, 0, 0, 0 } }; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); while ((c = getopt_long(argc, argv, "hnl:o:", longopts, NULL)) != -1) { switch(c) { case 'h': usage(stdout); break; case 'n': mode |= FALLOC_FL_KEEP_SIZE; break; case 'l': length = cvtnum(optarg); break; case 'o': offset = cvtnum(optarg); break; default: usage(stderr); break; } } if (length == -2LL) errx(EXIT_FAILURE, _("no length argument specified")); if (length <= 0) errx(EXIT_FAILURE, _("invalid length value specified")); if (offset < 0) errx(EXIT_FAILURE, _("invalid offset value specified")); if (optind == argc) errx(EXIT_FAILURE, _("no filename specified.")); fname = argv[optind++]; if (optind != argc) { warnx(_("unexpected number of arguments")); usage(stderr); } fd = open(fname, O_WRONLY|O_CREAT, 0644); if (fd < 0) err(EXIT_FAILURE, _("%s: open failed"), fname); #ifdef HAVE_FALLOCATE error = fallocate(fd, mode, offset, length); #else error = syscall(SYS_fallocate, fd, mode, offset, length); #endif /* * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported * ENOSYS: The filesystem does not support sys_fallocate */ if (error < 0) { if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP) errx(EXIT_FAILURE, _("keep size mode (-n option) unsupported")); err(EXIT_FAILURE, _("%s: fallocate failed"), fname); } close(fd); return EXIT_SUCCESS; }
int falloc(int fd, int len) { return fallocate(fd, 0, 0, len); }
int copy_file(const char *srcpath, const char *destpath) { const unsigned PAGESIZE = sysconf(_SC_PAGESIZE) * BUFSIZE; int srcfd = open(srcpath, O_RDONLY); if(srcfd == ERROR) { strexit(srcpath); } struct stat* st = (struct stat*) malloc(sizeof(struct stat)); if(fstat(srcfd, st) == ERROR) { strexit("FSTAT"); } int destfd = ERROR; if(S_ISDIR(st->st_mode)) { if(mkdir(destpath, st->st_mode) == ERROR) { strexit(destpath); } return 0; } else { if((destfd = open(destpath, O_WRONLY | O_CREAT | O_APPEND, st->st_mode)) == ERROR) { strexit(destpath); } } if(samefile(srcfd, destfd)) { return true; } if(ftruncate(destfd, 0) == ERROR) { strexit("TRUNCATE"); } if(st->st_size > 0 && fallocate(destfd, FALLOC_FL_KEEP_SIZE, 0, st->st_size) == ERROR) { strexit("FALLOCATE"); } // AIO Read / Write Information struct fileaio* arg = (struct fileaio*) malloc(sizeof(struct fileaio)); if(!arg) { strexit("FILEAIO MALLOC"); } arg->srcfd = srcfd; arg->destfd = destfd; arg->filesize = st->st_size; arg->offset = PAGESIZE; arg->aio = (struct aiocb*) calloc(1, sizeof(struct aiocb)); if(!arg->aio) { strexit("AIOCB CALLOC"); } arg->aio->aio_fildes = srcfd; arg->aio->aio_buf = malloc(PAGESIZE); if(!arg->aio->aio_buf) { strexit("AIOCB BUFFER"); } arg->aio->aio_nbytes = PAGESIZE; arg->aio->aio_offset = 0; // Setup sigevent handler for when AIO finishes arg->aio->aio_sigevent.sigev_notify = SIGEV_THREAD; arg->aio->aio_sigevent.sigev_notify_function = aio_handler; arg->aio->aio_sigevent.sigev_notify_attributes = NULL; arg->aio->aio_sigevent.sigev_value.sival_ptr = arg; if(aio_read(arg->aio) == ERROR) { strexit("READ LIO_LISTIO"); } ++outstanding_aio; return 0; }
int posix_fallocate(int fd, off64_t offset, off64_t len) { return fallocate(fd,0,offset,len); }
virtual int run(int checkpoint) override { int local_checkpoint = 0; //Open file foo const int fd_foo = open(foo_path.c_str(), O_RDWR); if (fd_foo < 0) { return -1; } // system("stat /mnt/snapshot/foo"); /*-------------------Variant 1 ---------------------------*/ //unaligned offset and size less than a page //Fallocate from off 17000 of length 3000 if(fallocate(fd_foo, FALLOC_FL_KEEP_SIZE ,17000, 3000) < 0){ close(fd_foo); return -2; } //fsync file_foo int res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 1 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 0; } //Expected output if checkpoint = 1 : Size = 16K, blocks == 40 (32 + 8) // system("stat /mnt/snapshot/foo"); /*-------------------Variant 2 ---------------------------*/ //aligned offset and size less than a page //Fallocate from off 24K of length 3000 if(fallocate(fd_foo, FALLOC_FL_KEEP_SIZE ,24576, 3000) < 0){ close(fd_foo); return -2; } //fsync file_foo res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 2 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 0; } //Expected output if checkpoint = 2: size = 16K, blocks == 48 (40 + 8) // system("stat /mnt/snapshot/foo"); /*-------------------Variant 3 ---------------------------*/ //Aligned offset and size > a page //Fallocate from off 32K of length 8192 if(fallocate(fd_foo, FALLOC_FL_KEEP_SIZE , 32768, 8192) < 0){ close(fd_foo); return -2; } //fsync file_foo res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 3 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 0; } //Expected output if checkpoint = 3: size = 16K, blocks == 64 (48 + 16) // system("stat /mnt/snapshot/foo"); /*-------------------Variant 4 ---------------------------*/ //unaligned offset and size less than a page //Fzero from off 42000 of length 3000 if(fallocate(fd_foo, FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE ,42000, 3000) < 0){ close(fd_foo); return -2; } //fsync file_foo res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 4 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 0; } //Expected output if checkpoint = 4 : Size = 16K, blocks == 72 (64 + 8) // system("stat /mnt/snapshot/foo"); /*-------------------Variant 5 ---------------------------*/ //aligned offset and size less than a page //Fzero from off 48K of length 3000 if(fallocate(fd_foo, FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE ,49152, 3000) < 0){ close(fd_foo); return -2; } //fsync file_foo res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 5 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 0; } //Expected output if checkpoint = 5: size = 16K, blocks == 80 (72 + 8) // system("stat /mnt/snapshot/foo"); /*-------------------Variant 6 ---------------------------*/ //Aligned offset and size > a page //Fzero from off 52K of length 8192 if(fallocate(fd_foo, FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE , 53248, 8192) < 0){ close(fd_foo); return -2; } //fsync file_foo res = fsync(fd_foo); if (res < 0){ close(fd_foo); return -3; } // Make a user checkpoint here. Checkpoint must be 6 beyond this point // Beyond this point, the effect of falloc must be visible if (Checkpoint() < 0){ close(fd_foo); return -4; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 1; } //Expected output if checkpoint = 6: size = 16K, blocks == 96 (80 + 16) // system("stat /mnt/snapshot/foo"); //Close open files close(fd_foo); return 0; }
static void mfd_assert_write(int fd) { ssize_t l; void *p; int r; /* * huegtlbfs does not support write, but we want to * verify everything else here. */ if (!hugetlbfs_test) { /* verify write() succeeds */ l = write(fd, "\0\0\0\0", 4); if (l != 4) { printf("write() failed: %m\n"); abort(); } } /* verify PROT_READ | PROT_WRITE is allowed */ p = mmap(NULL, mfd_def_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, mfd_def_size); /* verify PROT_WRITE is allowed */ p = mmap(NULL, mfd_def_size, PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, mfd_def_size); /* verify PROT_READ with MAP_SHARED is allowed and a following * mprotect(PROT_WRITE) allows writing */ p = mmap(NULL, mfd_def_size, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap() failed: %m\n"); abort(); } r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE); if (r < 0) { printf("mprotect() failed: %m\n"); abort(); } *(char *)p = 0; munmap(p, mfd_def_size); /* verify PUNCH_HOLE works */ r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, mfd_def_size); if (r < 0) { printf("fallocate(PUNCH_HOLE) failed: %m\n"); abort(); } }
bool __check_request(struct bufferevent* bev, struct evbuffer* in, struct evbuffer* out, struct nbd_client* client) { struct nbd_req_header* peek = NULL; struct nbd_req_header req; uint32_t err = 0; uint64_t handle = 0; void* test = NULL; char bytestr[32]; peek = (struct nbd_req_header*) evbuffer_pullup(in, sizeof(struct nbd_req_header)); if (peek) { memcpy(&req, peek, sizeof(struct nbd_req_header)); if (be32toh(req.magic) == GAMMARAY_NBD_REQ_MAGIC) { handle = be64toh(req.handle); switch(be32toh(req.type)) { case NBD_CMD_READ: err = __handle_read(peek, client); if (err == -1) return true; evbuffer_drain(in, sizeof(struct nbd_req_header)); __send_response(bev, err, handle, client->buf, be32toh(req.length)); return false; case NBD_CMD_WRITE: fprintf(stderr, "+"); err = __handle_write(peek, client, in); if (err == -1) return true; pretty_print_bytes(client->write_bytes, bytestr, 32); fprintf(stderr, "\twrite[%"PRIu64"]: %"PRIu64" cumulative " "bytes (%s)\n", client->write_count, client->write_bytes, bytestr); evbuffer_drain(in, be32toh(req.length)); __send_response(bev, err, handle, NULL, 0); return false; case NBD_CMD_DISC: fprintf(stderr, "got disconnect.\n"); client->state = NBD_DISCONNECTED; evbuffer_drain(in, sizeof(struct nbd_req_header)); nbd_ev_handler(bev, BEV_EVENT_EOF, client); return false; case NBD_CMD_FLUSH: fprintf(stderr, "got flush.\n"); evbuffer_drain(in, sizeof(struct nbd_req_header)); if (fsync(client->handle->fd)) __send_response(bev, errno, handle, NULL, 0); else __send_response(bev, 0, handle, NULL, 0); return false; case NBD_CMD_TRIM: fprintf(stderr, "got trim.\n"); evbuffer_drain(in, sizeof(struct nbd_req_header)); if (fallocate(client->handle->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, be64toh(req.offset), be32toh(req.length))) __send_response(bev, errno, handle, NULL, 0); else __send_response(bev, 0, handle, NULL, 0); return false; default: test = evbuffer_pullup(in, sizeof(struct nbd_req_header) + be32toh(req.length)); if (test) evbuffer_drain(in, sizeof(struct nbd_req_header) + be32toh(req.length)); fprintf(stderr, "unknown command!\n"); fprintf(stderr, "-- Hexdumping --\n"); if (test) hexdump(test, be32toh(req.length)); fprintf(stderr, "disconnecting, protocol error!\n"); client->state = NBD_DISCONNECTED; nbd_ev_handler(bev, BEV_EVENT_EOF, client); }; } } return true; }
static void mfd_fail_write(int fd) { ssize_t l; void *p; int r; /* verify write() fails */ l = write(fd, "data", 4); if (l != -EPERM) { printf("expected EPERM on write(), but got %d: %m\n", (int)l); abort(); } /* verify PROT_READ | PROT_WRITE is not allowed */ p = mmap(NULL, MFD_DEF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p != MAP_FAILED) { printf("mmap() didn't fail as expected\n"); abort(); } /* verify PROT_WRITE is not allowed */ p = mmap(NULL, MFD_DEF_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); if (p != MAP_FAILED) { printf("mmap() didn't fail as expected\n"); abort(); } /* Verify PROT_READ with MAP_SHARED with a following mprotect is not * allowed. Note that for r/w the kernel already prevents the mmap. */ p = mmap(NULL, MFD_DEF_SIZE, PROT_READ, MAP_SHARED, fd, 0); if (p != MAP_FAILED) { r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); if (r >= 0) { printf("mmap()+mprotect() didn't fail as expected\n"); abort(); } } /* verify PUNCH_HOLE fails */ r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, MFD_DEF_SIZE); if (r >= 0) { printf("fallocate(PUNCH_HOLE) didn't fail as expected\n"); abort(); } }
int main(int argc, char **argv) { int blocksize = 0; /* filesystem blocksize */ int fd; /* file descriptor */ int opt; int rc; char *fname; /* filename to map */ char *map = NULL; /* file map to generate */ int runs = -1; /* the number of runs to have */ int blocks = 0; /* the number of blocks to generate */ int maxblocks = 0; /* max # of blocks to create */ int prealloc = 1; /* whether or not to do preallocation */ int seed = 1; while ((opt = getopt(argc, argv, "m:r:s:p:")) != -1) { switch (opt) { case 'm': map = strdup(optarg); break; case 'p': #ifndef NO_FALLOCATE prealloc = atoi(optarg);; break; #else printf("Not built with preallocation support\n"); usage(); #endif /* sync file before mapping */ case 'r': runs = atoi(optarg); break; case 's': seed = atoi(optarg); break; default: usage(); } } if (runs != -1 && map) usage(); fname = argv[optind++]; if (!fname) usage(); fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("Can't open file"); exit(1); } if (ioctl(fd, FIGETBSZ, &blocksize) < 0) { perror("Can't get filesystem block size"); close(fd); exit(1); } #ifndef NO_FALLOCATE /* if fallocate passes, then we can do preallocation, else not */ if (prealloc) { prealloc = !((int) fallocate(fd, 0, 0, blocksize)); if (!prealloc) printf("preallocation not supported, disabling\n"); } #else prealloc = 0; #endif if (ftruncate(fd, 0)) { perror("Can't truncate file"); close(fd); exit(1); } if (map) { blocks = strlen(map); runs = 0; } srandom(seed); /* max file size 2mb / block size */ maxblocks = (2 * 1024 * 1024) / blocksize; if (runs == -1) printf ("Starting infinite run, if you don't see any output " "then its working properly.\n"); do { if (!map) { blocks = random() % maxblocks; if (blocks == 0) { printf("Skipping 0 length file\n"); continue; } map = generate_file_mapping(blocks, prealloc); if (!map) { printf("Could not create map\n"); exit(1); } } rc = create_file_from_mapping(fd, map, blocks, blocksize); if (rc) { perror("Could not create file\n"); free(map); close(fd); exit(1); } rc = compare_fiemap_and_map(fd, map, blocks, blocksize); if (rc) { printf("Problem comparing fiemap and map\n"); free(map); close(fd); exit(1); } free(map); map = NULL; if (ftruncate(fd, 0)) { perror("Could not truncate file\n"); close(fd); exit(1); } if (lseek(fd, 0, SEEK_SET)) { perror("Could not seek set\n"); close(fd); exit(1); } if (runs) runs--; } while (runs != 0); close(fd); return 0; }
void verify(const char *mntpt, size_t pgsize) { char filename[128]; size_t i; int e; pagesize = pgsize; work_alloc_start = 0; fd = -1; p = NULL; fprintf(stderr, "\nVerifying %s pagesize: %lu ...\n", mntpt, pagesize); snprintf(filename, sizeof(filename), "%s/t", mntpt); fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0644); if (fd==-1) err(errno, "open"); e = ftruncate(fd, FILEMAX); if (e) err(errno, "ftruncate"); p = mmap(NULL, VIRTMAX, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) err(errno, "mmap"); /* touch WORK */ fprintf(stderr, "allocating...\n"); for (i=0; i<WORK; i += pagesize) { p[i] = 1; //if (i%50 == 0) // usleep(1); } fprintf(stderr, "\t... done\n"); //assert(p[0] == 1); /* first pages may be already freed */ assert(p[work_alloc_start] == 1); /* hole punch touched memory */ fprintf(stderr, "deallocating...\n"); for (i=0; i<WORK; i += pagesize) { e = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, i, pagesize); if (e) err(errno, "fallocate"); //if (i%50 == 0) // usleep(1); } fprintf(stderr, "\t... done\n"); assert(p[0] == 0); /* changes must be forgotten */ assert(p[work_alloc_start] == 0); e = munmap(p, VIRTMAX); if (e) err(errno, "munmap"); e = close(fd); if (e) err(errno, "close"); fprintf(stderr, "OK\n"); }
int main(int argc, char *argv[]) { long hpage_size; int fd; int err; unsigned long free_before, free_after; test_init(argc, argv); hpage_size = check_hugepagesize(); fd = hugetlbfs_unlinked_fd(); if (fd < 0) FAIL("hugetlbfs_unlinked_fd()"); free_before = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); /* * First preallocate file with with just 1 byte. Allocation sizes * are rounded up, so we should get an entire huge page. */ err = fallocate(fd, 0, 0, 1); if (err) { if (errno == EOPNOTSUPP) IRRELEVANT(); if (err) FAIL("fallocate(): %s", strerror(errno)); } free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_before - free_after != 1) FAIL("fallocate 1 byte did not preallocate entire huge page\n"); /* * Now punch a hole with just 1 byte. On hole punch, sizes are * rounded down. So, this operation should not create a hole. */ err = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1); if (err) FAIL("fallocate(FALLOC_FL_PUNCH_HOLE): %s", strerror(errno)); free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_after == free_before) FAIL("fallocate hole punch 1 byte free'ed a huge page\n"); /* * Now punch a hole with of 2 * hpage_size - 1 byte. This size * should be rounded down to a single huge page and the hole created. */ err = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, (2 * hpage_size) - 1); if (err) FAIL("fallocate(FALLOC_FL_PUNCH_HOLE): %s", strerror(errno)); free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_after != free_before) FAIL("fallocate hole punch 2 * hpage_size - 1 byte did not free huge page\n"); /* * Perform a preallocate operation with offset 1 and size of * hpage_size. The offset should be rounded down and the * size rounded up to preallocate two huge pages. */ err = fallocate(fd, 0, 1, hpage_size); if (err) FAIL("fallocate(): %s", strerror(errno)); free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_before - free_after != 2) FAIL("fallocate 1 byte offset, huge page size did not preallocate two huge pages\n"); /* * The hole punch code will only delete 'whole' huge pags that are * in the specified range. The offset is rounded up, and (offset * + size) is rounded down to determine the huge pages to be deleted. * In this case, after rounding the range is (hpage_size, hpage_size). * So, no pages should be deleted. */ err = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 1, hpage_size); if (err) FAIL("fallocate(FALLOC_FL_PUNCH_HOLE): %s", strerror(errno)); free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_before - free_after != 2) FAIL("fallocate hole punch 1 byte offset, huge page size incorrectly deleted a huge page\n"); /* * To delete both huge pages, the range passed to hole punch must * overlap the allocated pages */ err = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 2 * hpage_size); if (err) FAIL("fallocate(FALLOC_FL_PUNCH_HOLE): %s", strerror(errno)); free_after = get_huge_page_counter(hpage_size, HUGEPAGES_FREE); if (free_after != free_before) FAIL("fallocate hole punch did not delete two huge pages\n"); PASS(); }
int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, off_t offset, bool fsdax) { unsigned char *addr = MAP_FAILED; struct sigaction act; unsigned x = x; void *buf; int rc; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 19, 0))) return 77; /* * MADV_HWPOISON must be page aligned, and this routine assumes * align is >= 8K */ if (align < SZ_2M) return 0; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus_hdl; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { fail(); rc = -errno; goto out; } /* dirty the block on disk to bypass the default zero page */ if (fsdax) { rc = pwrite(dax_fd, buf, 4096, offset + align / 2); if (rc < 4096) { fail(); rc = -ENXIO; goto out; } fsync(dax_fd); } addr = mmap(dax_addr, 2*align, PROT_READ|PROT_WRITE, MAP_SHARED_VALIDATE|MAP_POPULATE|MAP_SYNC, dax_fd, offset); if (addr == MAP_FAILED) { fail(); rc = -errno; goto out; } if (sigsetjmp(sj_env, 1)) { if (sig_mcerr_ar) { fprintf(stderr, "madvise triggered 'action required' sigbus\n"); goto clear_error; } else if (sig_count) { fail(); return -ENXIO; } } rc = madvise(addr + align / 2, 4096, MADV_HWPOISON); if (rc) { fail(); rc = -errno; goto out; } /* clear the error */ clear_error: if (!sig_mcerr_ar) { fail(); rc = -ENXIO; goto out; } if (!fsdax) { rc = 0; goto out; } rc = fallocate(dax_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset + align / 2, 4096); if (rc) { fail(); rc = -errno; goto out; } rc = pwrite(dax_fd, buf, 4096, offset + align / 2); if (rc < 4096) { fail(); rc = -ENXIO; goto out; } fsync(dax_fd); /* check that we can fault in the poison page */ x = *(volatile unsigned *) addr + align / 2; rc = 0; out: if (addr != MAP_FAILED) munmap(addr, 2 * align); free(buf); return rc; }
/***************************************************************************** * Calls the system call, with appropriate parameters and writes data ******************************************************************************/ void runtest(int mode, int fd, loff_t expected_size) { loff_t offset; loff_t len = block_size; loff_t write_offset, lseek_offset; offset = lseek(fd, 0, SEEK_END); struct stat file_stat; errno = 0; TEST(fallocate(fd, mode, offset, len)); /* check return code */ if (TEST_RETURN != 0) { if (TEST_ERRNO == EOPNOTSUPP || TEST_ERRNO == ENOSYS) { tst_brkm(TCONF, cleanup, "fallocate system call is not implemented"); } TEST_ERROR_LOG(TEST_ERRNO); tst_resm(TFAIL | TTERRNO, "fallocate(%d, %d, %" PRId64 ", %" PRId64 ") failed", fd, mode, offset, len); return; } else { if (STD_FUNCTIONAL_TEST) { /* No Verification test, yet... */ tst_resm(TPASS, "fallocate(%d, %d, %" PRId64 ", %" PRId64 ") returned %ld", fd, mode, offset, len, TEST_RETURN); } } if (fstat(fd, &file_stat) < 0) tst_resm(TFAIL | TERRNO, "fstat failed after fallocate()"); if (file_stat.st_size != expected_size) tst_resm(TFAIL | TTERRNO, "fstat test fails on fallocate (%d, %d, %" PRId64 ", %" PRId64 ") Failed on mode", fd, mode, offset, len); write_offset = random() % len; lseek_offset = lseek(fd, write_offset, SEEK_CUR); if (lseek_offset != offset + write_offset) { tst_resm(TFAIL | TTERRNO, "lseek fails in fallocate(%d, %d, %" PRId64 ", %" PRId64 ") failed on mode", fd, mode, offset, len); return; } //Write a character to file at random location TEST(write(fd, "A", 1)); /* check return code */ if (TEST_RETURN == -1) { TEST_ERROR_LOG(TEST_ERRNO); tst_resm(TFAIL | TTERRNO, "write fails in fallocate(%d, %d, %" PRId64 ", %" PRId64 ") failed", fd, mode, offset, len); } else { if (STD_FUNCTIONAL_TEST) { /* No Verification test, yet... */ tst_resm(TPASS, "write operation on fallocated(%d, %d, %" PRId64 ", %" PRId64 ") returned %ld", fd, mode, offset, len, TEST_RETURN); } } }
int parity_chsize(struct snapraid_parity* parity, data_off_t size, data_off_t* out_size, int skip_fallocate) { int ret; if (parity->st.st_size < size) { int f_ret; int f_errno; const char* call; #if HAVE_FALLOCATE call = "fallocate"; if (!skip_fallocate) { /* allocate real space using the specific Linux fallocate() operation. */ /* If the underline filesystem doesn't support it, this operation fails, */ /* instead posix_fallocate() fallbacks to write the whole file. */ ret = fallocate(parity->f, 0, 0, size); /* fallocate() returns the error number as positive integer, */ /* and in this case it doesn't set errno, just like posix_fallocate() */ /* Checking the glibc code (2.11.1 and 2.14.1) it seems that ENOSYS */ /* may be returned in errno, so we support both the return way */ if (ret > 0) { /* if a positive error is returned, convert it to errno */ /* LCOV_EXCL_START */ errno = ret; ret = -1; /* LCOV_EXCL_STOP */ } } else { errno = EOPNOTSUPP; ret = -1; } /* we get EOPNOTSUPP if the operation is not supported, like in ext3/ext2 */ /* or ENOSYS with kernel before 2.6.23 */ if (errno == EOPNOTSUPP || errno == ENOSYS) { /* fallback using ftruncate() */ call = "ftruncate"; ret = ftruncate(parity->f, size); } #else (void)skip_fallocate; /* avoid the warning */ /* allocate using a sparse file */ call = "ftruncate"; ret = ftruncate(parity->f, size); #endif /* save the state of the grow operation */ f_ret = ret; f_errno = errno; /* get the stat info */ ret = fstat(parity->f, &parity->st); if (ret != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Error accessing parity file '%s'. %s.\n", parity->path, strerror(errno)); goto bail; /* LCOV_EXCL_STOP */ } /* return the new size */ *out_size = parity->st.st_size; /* now check the error */ if (f_ret != 0) { /* LCOV_EXCL_START */ if (f_errno == ENOSPC) { fprintf(stderr, "Failed to grow parity file '%s' to size %"PRIu64" using %s due lack of space.\n", parity->path, size, call); } else { fprintf(stderr, "Error growing parity file '%s' to size %"PRIu64" using %s. Do you have enough space? %s.\n", parity->path, size, call, strerror(f_errno)); } goto bail; /* LCOV_EXCL_STOP */ } } else if (parity->st.st_size > size) { int f_ret; int f_errno; /* truncate the parity file */ ret = ftruncate(parity->f, size); /* save the state of the shrink operation */ f_ret = ret; f_errno = errno; /* get the stat info */ ret = fstat(parity->f, &parity->st); if (ret != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Error accessing parity file '%s'. %s.\n", parity->path, strerror(errno)); goto bail; /* LCOV_EXCL_STOP */ } /* return the new size */ *out_size = parity->st.st_size; /* now check the error */ if (f_ret != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Error truncating parity file '%s' to size %"PRIu64". %s.\n", parity->path, size, strerror(f_errno)); goto bail; /* LCOV_EXCL_STOP */ } /* adjust the valid to the new size */ parity->valid_size = size; } return 0; bail: /* LCOV_EXCL_START */ parity->valid_size = 0; return -1; /* LCOV_EXCL_STOP */ }
virtual int run( int checkpoint ) override { test_path = mnt_dir_ ; A_path = mnt_dir_ + "/A"; AC_path = mnt_dir_ + "/A/C"; B_path = mnt_dir_ + "/B"; foo_path = mnt_dir_ + "/foo"; bar_path = mnt_dir_ + "/bar"; Afoo_path = mnt_dir_ + "/A/foo"; Abar_path = mnt_dir_ + "/A/bar"; Bfoo_path = mnt_dir_ + "/B/foo"; Bbar_path = mnt_dir_ + "/B/bar"; ACfoo_path = mnt_dir_ + "/A/C/foo"; ACbar_path = mnt_dir_ + "/A/C/bar"; int local_checkpoint = 0 ; int fd_foo = cm_->CmOpen(foo_path.c_str() , O_RDWR|O_CREAT , 0777); if ( fd_foo < 0 ) { cm_->CmClose( fd_foo); return errno; } if ( WriteData ( fd_foo, 0, 32768) < 0){ cm_->CmClose( fd_foo); return errno; } if ( fallocate( fd_foo , FALLOC_FL_KEEP_SIZE , 32768 , 32768) < 0){ cm_->CmClose( fd_foo); return errno; } int fd_test = cm_->CmOpen(test_path.c_str() , O_DIRECTORY , 0777); if ( fd_test < 0 ) { cm_->CmClose( fd_test); return errno; } if ( cm_->CmFsync( fd_test) < 0){ return errno; } if ( cm_->CmCheckpoint() < 0){ return -1; } local_checkpoint += 1; if (local_checkpoint == checkpoint) { return 1; } if ( cm_->CmClose ( fd_foo) < 0){ return errno; } if ( cm_->CmClose ( fd_test) < 0){ return errno; } return 0; }
void runSuccess() { int fd = VALID_FD; if (fd >= 0) { fallocate(fd, anyint(), anyint(), anyint()); } }
static int eat(const char *filename) { #define fail_if(cond) \ while (cond) \ { \ show_error(filename); \ if (fd != -1) \ close(fd); \ return -1; \ } #define check_io(i, j) \ while ((size_t) (i) != (size_t) (j)) \ { \ if ((i) >= 0) \ errno = EIO; \ fail_if(1); \ } const int fd = open(filename, O_RDWR); fail_if(fd == -1); const off_t file_size = lseek(fd, 0, SEEK_END); fail_if(file_size == -1); int rc; off_t offset; ssize_t r_bytes, w_bytes; offset = lseek(fd, 0, SEEK_SET); fail_if(offset == -1); if (block_size == 0) { struct statvfs st; rc = fstatvfs(fd, &st); fail_if(rc == -1); if (st.f_bsize == 0 || st.f_bsize >= block_size_limit) { errno = ERANGE; fail_if(1); } block_size = st.f_bsize; } const off_t n_blocks = (file_size + block_size - 1) / block_size; const size_t tail_size = (size_t) (1 + (file_size - 1) % block_size); switch (n_blocks) { case 2: r_bytes = read(fd, buffer, block_size); check_io(r_bytes, block_size); w_bytes = write(STDOUT_FILENO, buffer, block_size); check_io(w_bytes, block_size); case 1: r_bytes = read(fd, buffer, tail_size); check_io(r_bytes, tail_size); w_bytes = write(STDOUT_FILENO, buffer, (size_t) r_bytes); check_io(w_bytes, r_bytes); case 0: goto done; default: break; } struct stat stat; rc = fstat(fd, &stat); fail_if(rc == -1); if (stat.st_nlink > 1 && !opt_force) { errno = EMLINK; fail_if(1); } for (off_t i = 0; i < n_blocks / 2; i++) { r_bytes = read(fd, buffer, block_size); check_io(r_bytes, block_size); w_bytes = write(STDOUT_FILENO, buffer, block_size); check_io(w_bytes, block_size); if (i == 0 && opt_punch) { #if HAVE_FALLOC_FL_PUNCH_HOLE rc = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, offset, block_size); if (rc == 0) { offset = 0; while (1) { r_bytes = read(fd, buffer, block_size); if (r_bytes == 0) break; offset += r_bytes; fail_if(r_bytes == -1); w_bytes = write(STDOUT_FILENO, buffer, (size_t) r_bytes); check_io(w_bytes, r_bytes); rc = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, offset); fail_if(rc != 0); } goto done; } else #else errno = ENOTSUP; #endif { show_error(filename); if (opt_punch > 1) { fprintf(stderr, "hungrycat: %s: fallocate() failed\n", filename); close(fd); return 1; } else fprintf(stderr, "hungrycat: %s: fallocate() failed; falling back to ftruncate()\n", filename); } } offset = (n_blocks - i - 1) * block_size; r_bytes = pread(fd, buffer, block_size, offset); check_io(r_bytes, (i == 0 ? tail_size : block_size)); w_bytes = pwrite(fd, buffer, (size_t) r_bytes, i * block_size); check_io(r_bytes, w_bytes); rc = ftruncate(fd, offset); fail_if(rc == -1); } if ((n_blocks & 1) == 1) { r_bytes = read(fd, buffer, block_size); check_io(r_bytes, block_size); w_bytes = write(STDOUT_FILENO, buffer, block_size); check_io(w_bytes, block_size); rc = ftruncate(fd, (n_blocks / 2) * block_size); fail_if(rc == -1); } for (off_t i = (n_blocks / 2) - 1; i > 0; i--) { r_bytes = pread(fd, buffer, block_size, i * block_size); check_io(r_bytes, block_size); w_bytes = write(STDOUT_FILENO, buffer, block_size); check_io(w_bytes, block_size); rc = ftruncate(fd, i * block_size); fail_if(rc == -1); } assert(n_blocks > 0); r_bytes = pread(fd, buffer, tail_size, 0); check_io(r_bytes, tail_size); w_bytes = write(STDOUT_FILENO, buffer, (size_t) r_bytes); check_io(w_bytes, r_bytes); done: rc = unlink(filename); fail_if(rc == -1); rc = close(fd); { const int fd = -1; fail_if(rc == -1); } return 0; #undef fail_if }
int main(int argc, char *argv[]) { int i; FILE *fp; int fd; char *path = NULL; size_t off = 0, len = 0; char optchr; enum EnumMode mode = EnumModePosix; while (-1 != (optchr = getopt(argc, argv, "plLh"))) { switch (optchr) { case EnumModePosix: case EnumModeLinux: case EnumModeLinuxWithFlag: mode = optchr; break; case 'h': default: printUsage(); } } for (i = optind; i < argc; ++i) { if (NULL == path) path = argv[i]; else if ( ! len) len = myAtoi(argv[i]); else if ( ! off) off = myAtoi(argv[i]); } if (NULL == path) printUsage(); if (0 == len) len = (size_t)100 * 1024 * 1024; fp = fopen(path, "w"); if (NULL == fp) myPerror("fopen", 42); fd = fileno(fp); if (EnumModePosix == mode) { const int rc = posix_fallocate(fd, off, len); if (RC_OK != rc) { char buf[BUFLEN]; fprintf(stderr, "posix_fallocate: %s", strerror_r(rc, buf, sizeof buf)); exit(40); } } else { const int flag = (EnumModeLinuxWithFlag == mode ? FALLOC_FL_KEEP_SIZE : 0); if (RC_NG == fallocate(fd, flag, off, len)) myPerror("fallocate", 44); } fclose(fp); return 0; }