Consider i have the following piece of code:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv) {
if (argc > 2) {
int fd = open(argv[1], O_CREAT|O_WRONLY, 0777);
size_t size = atoi(argv[2]);
if (fd > 0) {
//int result = fallocate(fd, 0, 0, size);
//printf("itak: %d, errno: %d (%s)\n", result, errno, strerror(errno));
int result = posix_fallocate(fd, 0, size);
printf("itak: %d, errno: %d (%s)\n", result, result, strerror(result));
} else {
printf("failed opening file\n");
}
} else {
printf("Usage blah\n");
}
}
Which is a simple version of /usr/bin/fallocate i made to test my assumptions. I found out that if i use it to create a file which is larger than the filesystem free space it will return -1 and a proper errno, but will still create a file of a maximum allowed size. This seems strange to me, because the command explicitly returned -1, which should be a signal for a failure, but it still did something. And moreover it did not what i asked for - it created a file of an unknown (at the moment i run it) size. If i use fallocate() to reserve some space for, i dunno, kitten photos it would be useless for me if it reserves less space than i asked.
Yes, fallocate() and posix_fallocate() behave the save way, I checked both as you can see.
Naturally i thought i am doing something wrong. Because if you run into problems while programming that is what happens in 99.9%. So i tried the /usr/bin/fallocate utility and yes, it "failes", but still creates a file.
Here is an example of me running the utility:
rakul@lucky-star /tmp $ strace fallocate -l 10G /tmp/test 2>&1 | grep fallocate
execve("/usr/bin/fallocate", ["fallocate", "-l", "10G", "/tmp/test"], [/* 47 vars */]) = 0
fallocate(3, 0, 0, 10737418240) = -1 ENOSPC (No space left on device)
write(2, "fallocate: ", 11fallocate: ) = 11
write(2, "fallocate failed", 16fallocate failed) = 16
rakul@lucky-star /tmp $ ls -l /tmp/test
-rw-r--r-- 1 rakul rakul 9794732032 сен 26 19:15 /tmp/test
rakul@lucky-star
As you can see it had no specific mode set on the fallocate() call, it failed, but the file got created, of an unexpected size.
I found out that some guys on the internet see the opposite behavior:
rxxxx@home/tmp> fallocate -l 10G test.img
fallocate: fallocate failed: На устройстве не осталось свободного места
rxxxx@home/tmp> ls -l test.img
-rw-r--r-- 1 rogue rogue 0 Врс 26 17:36 test.img
(it says "not enough space left" in russian)
I have tried ext4 and tmpfs with same results. I have gentoo linux, 3.18 kernel. However originally i have seen this issue on an up-to-date SLES12.
My question is: why there are different results and how can i prevent /usr/bin/fallocate or fallocate() from creating a file in case there is not enough a
Reading "man 2 fallocate", NO warranty is given about the behavior of the library call in the case where the disk runs out of space, save that it will return -1 and the error will be ENOSPC
.
In POSIX.1-2001, no side-effects-free requirement is placed on the posix_fallocate
call either.
So the implementation is entitled to create a half-sized file if it wishes to do so.
What's probably going on under the hood is that a chunk of space of a certain size is requested from the filesystem and put into the file. Then another chunk is requested, and so on until the file is sufficiently large to meet the caller's needs. Thus, if the filesystem runs out of space midway through, a file of a size less than requested is left behind.
You are just going to have to deal with the behavior of the call as is; you cannot change the implementation code (well, you could by submitting a patch!). It's much easier to change your program to handle all the allowed failure modes properly.