2

Updated: This FALLOC_FL_PUNCH_HOLE is not originally supported by 3.0.0-17, I think I need to patch it.

I know linux have this hole feature, and I am wondering if I could make a hole in a existing file.

For specific, I have created a file named hole_test by these codes:

 18 #include <stdlib.h>
 19 #include <sys/types.h>
 20 #include <sys/stat.h>
 21 #include <fcntl.h>
 22 #include <unistd.h>
 23 #include <errno.h>
 24 
 25 int main(int argc, char **argv)
 26 {
 27     int fd;
 28     char a[7]="happy!";
 30     fd = open("hole_test", O_CREAT|O_RDWR, 0666);
 31     if(fd == -1)
 32             printf("error, %s\n", (char *)strerror(errno));
 35 //  fallocate(fd, 0x02, 0,0);
 36 //  pwrite(fd, a, sizeof(a), 0);
 37 //  pwrite(fd, a, sizeof(b), 65536);
 38     close(fd);
 39     return 0;
 40 }

firstly, I user L36 L37 to create a file. the stat hole_test shows that:

  File: `hole_test'
  Size: 65540       Blocks: 16         IO Block: 4096   regular file
Device: 801h/2049d  Inode: 1052101     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   bxshi)   Gid: ( 1000/   bxshi)
Access: 2012-04-03 23:02:35.227664608 +0800
Modify: 2012-04-03 23:02:35.227664608 +0800
Change: 2012-04-03 23:02:35.227664608 +0800

Then I use L35 and comment L36 L37 to make a hole in my file.(0x02 equals to FALLOC_FL_PUNCH_HOLE, I did not find where it is defined so just use its value)

and then, by stat hole_test, the Blocks is still 16.

  File: `hole_test'
  Size: 65540       Blocks: 16         IO Block: 4096   regular file
Device: 801h/2049d  Inode: 1052101     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   bxshi)   Gid: ( 1000/   bxshi)
Access: 2012-04-03 23:02:35.227664608 +0800
Modify: 2012-04-03 23:02:35.227664608 +0800
Change: 2012-04-03 23:02:35.227664608 +0800

I want to know if I could make new holes in this hole_test file to erase existing data?

How could I make a hole in hole_test at offset 0 to 7, in that way I think the Blocks could become 8 and the string I wrote would disappear.

Hope you guys could know what I said and give me some advice.

xryl669
  • 3,376
  • 24
  • 47
bxshi
  • 2,252
  • 5
  • 31
  • 50
  • just tried fopen() and then fseek() to move the cursor to the right offset? After fseek() you can fwrite() to change blocks of data. – dAm2K Apr 03 '12 at 14:18
  • I do not want just change the data, but want to make holes in there. I mean I want to find a way to change this 16 blocks file to 8 blocks. – bxshi Apr 03 '12 at 14:20
  • 1
    This post could help [file hole](http://stackoverflow.com/questions/5315428/how-to-create-a-file-with-file-holes). – rbelli Apr 03 '12 at 14:25
  • Note: `man fallocate` ==> "Not all filesystems support FALLOC_FL_PUNCH_HOLE; if a filesystem doesn't support the operation, an error is returned." So you may want to check if the underlying FS allows creation of sparse files (hole files). – askb Dec 10 '14 at 04:47

2 Answers2

3

You use fallocate(fd, FALLOC_FL_PUNCH_HOLE, offset, len). (Supported since Linux 2.6.38) See https://lwn.net/Articles/415889/ for behind-the-scenes details and accompanying patches.

jørgensen
  • 10,149
  • 2
  • 20
  • 27
  • got a question, where is this `FALLOC_FL_PUNCH_HOLE` defined? I check the `man`, but do not find any header include this constant. – bxshi Apr 03 '12 at 14:53
  • I find out that `FALLOC_FL_PUNCH_HOLE` is equals to `0x02`, so I tried `fallocate(fd, FALLOC_FL_PUNCH_HOLE, 0, 7);`. And it is still 16 blocks. No difference on that. Do I need to patch this manually? I am using 3.x Kernel. – bxshi Apr 03 '12 at 15:04
  • Check the return and error code from `fallocate`. As mentioned in the link, only a few filesystems support it. – jørgensen Apr 04 '12 at 16:24
  • yes, thanks. I checked it, and the `errno` indicates that `Operation not supported` The fs of my hd is `EXT4`. And the `man 2 fallocate` only have one flag `FALLOC_FL_KEEP_SIZE`. – bxshi Apr 04 '12 at 16:43
  • @bxshi `FALLOC_FL_PUNCH_HOLE` MUST be OR'ed with the `FALLOC_FL_KEEP_SIZE0` flag . You cannot decrease a filesize with FALLOC_FL_PUNCH_HOLE – nos Sep 14 '15 at 14:48
0

According to the man page for fallocate(2):

The FALLOC_FL_PUNCH_HOLE flag must be ORed with FALLOC_FL_KEEP_SIZE in mode; in other words, even when punching off the end of the file, the file size (as reported by stat(2)) does not change.

At least on ext4, if you just pass FALLOC_FL_PUNCH_HOLE, fallocate() will return Operation not supported.

Also note that:

The FALLOC_FL_* flags are defined in glibc headers only since version 2.18.

So you may need to manually define them if you're using a supported kernel with an earlier version of libc:

// Constants are defined in glibc >= 2.18
#define FALLOC_FL_KEEP_SIZE    0x1
#define FALLOC_FL_PUNCH_HOLE   0x2
Greg
  • 1
  • 1