Advertisement
drankinatty

C - copy files using sendfile with offset and nbytes options

Jun 15th, 2017
392
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.51 KB | None | 0 0
  1. /* simple copy of 'srcfile' to 'destfile' using sendfile
  2.  *
  3.  * options:
  4.  *   -h           display help
  5.  *   -o offset    begin copy from srcfile at offset-bytes
  6.  *   -n nbytes    copy only nbytes-bytes from src to dest
  7.  *   -v           display verbose ouotput
  8.  *
  9.  */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <errno.h>
  13. #include <fcntl.h>
  14. #include <sys/sendfile.h>
  15. #include <sys/stat.h>
  16. #include <sys/types.h>
  17. #include <unistd.h>
  18.  
  19. #define PACKAGE "sfcopy"
  20. #define VERSION "0.01"
  21. #define MAXARGS 10
  22.  
  23. /* note: on non-GNU systems, implement macros as functions, the
  24.  * braced grouping is not ISO-C
  25.  */
  26.  
  27. /* simple macro to open file and validate the return */
  28. #define xopenro(fn, flags)    \
  29. ({  int fd = open (fn, flags);    \
  30.     if (fd == -1) {  \
  31.         fprintf (stderr, "xopen() error: file open failed '%s'.\n", fn);   \
  32.         exit (EXIT_FAILURE);    \
  33.     }   \
  34.     fd; \
  35. })
  36.  
  37. /* simple macro to open file with creation mode flags and
  38.  * validate the return of open
  39.  */
  40. #define xopencrt(fn, flags, mode)    \
  41. ({  int fd = open (fn, flags, mode);    \
  42.     if (fd == -1) {  \
  43.         fprintf (stderr, "xopen() error: file open failed '%s'.\n", fn);   \
  44.         exit (EXIT_FAILURE);    \
  45.     }   \
  46.     fd; \
  47. })
  48.  
  49. int processopts (int argc, char **argv, long (*lv)[10]);
  50. long file_sfcopy (char *ofn, char *ifn, off_t *offset, long *nbytes);
  51. void help (int xcode);
  52.  
  53. int main (int argc, char **argv) {
  54.  
  55.     long lv[MAXARGS] = {0};
  56.     int optind = processopts (argc, argv, &lv);
  57.     off_t offset = lv[0];
  58.     long nbytes = lv[1], verbose = lv[2];
  59.     char *ifn = argc > optind ? argv[optind] : NULL,
  60.          *ofn = argc > optind + 1 ? argv[optind + 1] : NULL;
  61.  
  62. #ifdef DEBUG
  63.     printf (" nbytes  : %ld\n verbose : %ld\n infile  : %s\n outfile : %s\n\n",
  64.             nbytes, verbose, ifn ? ifn : "stdin", ofn ? ofn : "stdout");
  65. #endif
  66.  
  67.     if (verbose) {  /* provide verbose output */
  68.         printf ("copying  %s -> %s\n", ifn ? ifn : "stdin",
  69.                 ofn ? ofn : "stdout");
  70.     }
  71.  
  72.     if (file_sfcopy (ofn, ifn, &offset, &nbytes) == -1) {
  73.         fprintf (stderr, "error: file_sfcopy returned -1\n");
  74.     }
  75.  
  76.     if (verbose) printf ("%ld bytes copied.\n", nbytes);
  77.  
  78.     return 0;
  79. }
  80.  
  81. /** copy input file name 'ifn' to output file name 'ofn'
  82.  *  using sendfile to perform copy in kernel space. copy
  83.  *  speed is equivalent to memcopy of mmap'ed files.
  84.  *  returns the number of bytes copied on success, -1
  85.  *  otherwise. (include: off_t offset to start copy at
  86.  *  location other than beginning of 'ifn'.
  87.  */
  88. long file_sfcopy (char *ofn, char *ifn, off_t *offset, long *nbytes)
  89. {
  90.     int ifd, ofd, writeok = 1;
  91.     struct stat sb = {0};
  92.  
  93.     /* open input file read-only, assign file descriptor */
  94.     ifd = ifn ? xopenro (ifn, O_RDONLY) : STDIN_FILENO;
  95.  
  96.     if (fstat (ifd, &sb) == -1) {   /* fill stat buffer */
  97.         fprintf (stderr, "error: undetermined input file size.\n");
  98.         return -1;
  99.     }
  100.  
  101.     /* open output file for writing with same ownership/permission */
  102.     ofd = ofn ? xopencrt (ofn, O_RDWR|O_CREAT|O_TRUNC, sb.st_mode) :
  103.                 STDOUT_FILENO;
  104.  
  105.     if (!ifd || !ofd) { /* validate file descriptors */
  106.         fprintf (stderr, "error: invalid file descriptor.\n");
  107.         return -1;
  108.     }
  109.  
  110.     /* validate offset less than st_size */
  111.     if (*offset >= sb.st_size) {
  112.         fprintf (stderr, "error: requested offset exceeds file size.\n");
  113.         return -1;
  114.     }
  115.  
  116.     /* copy lesser of *nbytes or st_size. (default: st_size) */
  117.     *nbytes = *nbytes + *offset > sb.st_size || !(*nbytes) ?
  118.               sb.st_size - *offset : *nbytes;
  119.  
  120.     /* use sendfile to copy input to output */
  121.     if (sendfile (ofd, ifd, offset, *nbytes) != *nbytes) {
  122.         fprintf (stderr, "error: sendfile write failure.\n");
  123.         writeok = 0;
  124.     }
  125.  
  126.     /* if to stdout, tidy up output to insure final newline written */
  127.     if (ofd == STDOUT_FILENO) {     /* check last char */
  128.         if (lseek (ifd, *offset > 0 ? *offset - 1 : 0, SEEK_SET) != -1) {
  129.             int c;      /* if not '\n' write newline to stdout */
  130.             if (read (ifd, &c, 1) != -1 && c != '\n')
  131.                 putchar ('\n');
  132.         }
  133.         else
  134.             fprintf (stderr, "error: lseek of ifd failed.\n");
  135.     }
  136.     else /* validate close of file descriptors */
  137.         if (close (ofd) == EIO)
  138.             fprintf (stderr, "error: I/O error on output descriptor close.\n");
  139.     if (ifd != STDIN_FILENO)
  140.         if (close (ifd) == EIO) /* validate close of file descriptors */
  141.             fprintf (stderr, "error: I/O error on input descriptor close.\n");
  142.  
  143.     /* without sdtout cleanup, simply close files validating close, e.g. */
  144.     /*
  145.     if (ifd != STDIN_FILENO)
  146.         if (close (ifd) == EIO)
  147.             fprintf (stderr, "error: I/O error on input descriptor close.\n");
  148.     if (ofd == STDOUT_FILENO)
  149.         if (close (ofd) == EIO)
  150.             fprintf (stderr, "error: I/O error on output descriptor close.\n");
  151.     */
  152.  
  153.     return writeok ? *nbytes : -1;
  154. }
  155.  
  156. /* process -h, -o offset, -n nbytes and -v options */
  157. int processopts (int argc, char **argv, long (*lv)[10])
  158. {
  159.     int opt;
  160.    
  161.     while ((opt = getopt (argc, argv, "ho:n:v")) != -1) {
  162.         switch (opt) {
  163.             case 'h':
  164.                 help (0);
  165.             case 'o':   /* set offset */
  166.                 (*lv)[0] = strtol (optarg, NULL, 10);
  167.                 break;
  168.             case 'n':   /* set number of bytes */
  169.                 (*lv)[1] = strtol (optarg, NULL, 10);
  170.                 break;
  171.             case 'v':   /* set verbose */
  172.                 (*lv)[2] = 1;
  173.                 break;
  174.         }
  175.     }
  176.  
  177.     if (argc < 2) { /* if insufficient input */
  178.         fprintf (stderr, "error: insufficient arguments.\n");
  179.         help (1);
  180.     }
  181.        
  182.     return optind;  /* return next argument index */
  183. }
  184.  
  185. /* simple help function, exits with xcode */
  186. void help (int xcode)
  187. {
  188.     printf ("\n %s, version %s\n\n"
  189.             "  usage:  %s [-honv] srcfile destfile\n\n"
  190.             "  copies srcfile to destfile using sendfile (limit: 2,147,479,552"
  191.             " bytes).\n"
  192.             "  if only srcfile is given, copy to stdout.\n\n"
  193.             "    Options:\n"
  194.             "      -h           this help.\n"
  195.             "      -o offset    begin copy from srcfile at offset-bytes.\n"
  196.             "      -n nbytes    copy only nbytes-bytes from src to dest.\n"
  197.             "      -v           verbose ouotput.\n\n",
  198.             PACKAGE, VERSION, PACKAGE);
  199.  
  200.     exit (xcode);
  201. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement