Copying Sparse Files - Extended C Example Application

Thanks to some interesting input, I decided that I should extend upon my file copy previous example. In case, you are wondering, I chose to use fwrite, fread etc.. on this application, but the same is possible with read/write to copy a "sparse file" or a file with "file holes"

As per input, here is how I create my sparse file using the dd command:

  1. $ dd if=/dev/zero of=sparse.img bs=1 count=0 seek=128M
  2. 0+0 records in
  3. 0+0 records out
  4. 0 bytes (0 B) copied, 0.000530044 s, 0.0 kB/s
  1. $ du -h  sparse.img ; du -h --apparent-size sparse.img
  2. 0    sparse.img
  3. 128M sparse.img

Then after compiling and executing my application, I see:

  1. gcc -Wall -o cpy cpy2.c
  2. ./cpy sparse.img output.img
  1. ]du -h  output.img; du -h --apparent-size output.img
  2. 0 output.img
  3. 128M    output.img

  1. /**
  2.  * @file cpy2.c
  3.  * @author Ron Brash ([email protected]
  4.  * @date Sept 22, 2014
  5.  * @brief C program demonstrating how to duplicate a file which
  6.  * contains a "file hole" or in other words - a "sparse file".
  7.  */
  8. #define _GNU_SOURCE
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <unistd.h>
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. #include <fcntl.h>
  15. #include <error.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include <ctype.h>
  19. #include <stdint.h>
  20.  
  21. #include <assert.h>
  22.  
  23. static off_t fsize(const char *filename);
  24.  
  25. /**
  26.  * fsize(const char *filename)
  27.  *
  28.  * @brief Returns the filesize of a file using stat.h
  29.  * @param filename
  30.  * @return -1 if there is an error, zero or positive value otherwise
  31.  */
  32. static off_t fsize(const char *filename)
  33. {
  34.         struct stat st = { 0 };
  35.  
  36.         if (stat(filename, &st) == 0) {
  37.  
  38.                 return st.st_size;
  39.         }
  40.  
  41.         return (-1);
  42. }
  43.  
  44. /**
  45.  * main(int argc, char **argv)
  46.  *
  47.  * @brief A main function
  48.  * @param argc
  49.  * @param argv
  50.  * @return -1 if error, 0 for success
  51.  */
  52. int main(int argc, char **argv)
  53. {
  54.         FILE *inputFP = NULL;
  55.         FILE *outputFP = NULL;
  56.         char buf[BUFSIZ] = { };
  57.         register ssize_t file_size = 0;
  58.         ssize_t apparent_size = 0;
  59.  
  60.         /* Open a file descriptor for the input file */
  61.         if ((inputFP = fopen(argv[1], "r")) == NULL) {
  62.                 perror("Error opening input file descriptor");
  63.                 return (-1);
  64.         }
  65.  
  66.         /* Determine the file_size of the input file */
  67.         if ((apparent_size = fsize(argv[1])) < 0) {
  68.                 perror("Unable to determine accurate size of file");
  69.                 return (-1);
  70.         }
  71.         printf("file's apparent size:%i\n", (unsigned int)apparent_size);
  72.  
  73.         /* Open a file descriptor for the output file */
  74.         if ((outputFP = fopen(argv[2], "w")) == NULL) {
  75.                 perror("Error opening output file descriptor");
  76.                 return (-1);
  77.         }
  78.  
  79.         /* Advise the kernel that there will be a long sequential write */
  80.         if (posix_fadvise(fileno(inputFP), 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
  81.                 perror("Unable to advise the kernel for the upcoming sequential write");
  82.                 // Continue anyways...
  83.         }
  84.  
  85.         /* Create a file using ftruncate and lets carry on */
  86.         if (ftruncate(fileno(outputFP), apparent_size) < 0) {
  87.                 perror("Unable to create a file that will have a similar size to the original");
  88.                 return (-1);
  89.         }
  90.  
  91.         register int i = 0;
  92.         register  unsigned long holeSize = 0;
  93.         while ((file_size = fread(buf, sizeof(char), sizeof(buf), inputFP)) > 0) {
  94.  
  95.                 for (i = 0; i < file_size; i++) {
  96.  
  97.                         if (buf[i] == '\0') {
  98.                                 holeSize++;
  99.                                 continue;
  100.                         }
  101.  
  102.                         fseek(outputFP, holeSize, SEEK_CUR);
  103.                         fwrite(&buf[i], 1, 1, outputFP);
  104.  
  105.                         if (ferror(outputFP)) {
  106.                                 perror("write\n");
  107.                                 break;
  108.                         }
  109.                         holeSize = 0;
  110.  
  111.                 }
  112.  
  113.         }
  114.  
  115.         /* Close the input file descriptor */
  116.         if (fclose(inputFP) < 0) {
  117.                 perror("closing inputFP");
  118.                 return (-1);
  119.         }
  120.  
  121.         /* Close the output file descriptor */
  122.         if (fclose(outputFP) < 0) {
  123.                 perror("closing outputFP");
  124.                 return (-1);
  125.         }
  126.  
  127.         return 0;
  128. }

Blog tags: 

AttachmentSize
Sparse File copy application in C2.77 KB

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.