Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallocate on a file with size equal to the length #6860

Closed
CMCDragonkai opened this issue Nov 13, 2017 · 4 comments
Closed

Fallocate on a file with size equal to the length #6860

CMCDragonkai opened this issue Nov 13, 2017 · 4 comments

Comments

@CMCDragonkai
Copy link

Reposted from #326 (comment)

I'm using 0.7.2-1, and I noticed that if you run posix_fallocate on a file with the same size as the length specified, it returns with EBADF. This doesn't happen when I do it on tmpfs.

//usr/bin/env make -s "${0%.*}" && ./"${0%.*}" "$@"; s=$?; rm ./"${0%.*}"; exit $s

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main () {
  int fd = open("./randomfile", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1) {
    perror("open()");
  }
  int status = posix_fallocate(fd, 0, 100);
  if (status != 0) {
    printf("%s\n", strerror(status));
  }
  return 0;
}

Running the above on an empty or non-existent file works fine, as soon as you run it again, it fails with EBADF. This is bit strange behaviour.

@loli10K
Copy link
Contributor

loli10K commented Nov 13, 2017

This also happens for files with different size:

root@linux:~# truncate -s 64m /var/tmp/issue-6860
root@linux:~# zpool create -m /issue-6860 issue-6860 /var/tmp/issue-6860
root@linux:~# cp -v posix_fallocate.c /issue-6860
‘posix_fallocate.c’ -> ‘/issue-6860/posix_fallocate.c’
root@linux:~# cd /issue-6860
root@linux:/issue-6860# cat posix_fallocate.c 
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char **argv) {
  int len = atoi(argv[1]);
  int fd = open("./randomfile", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1) {
    perror("open()");
  }
  int status = posix_fallocate(fd, 0, len);
  if (status != 0) {
    printf("%s\n", strerror(status));
  }
  return 0;
}
root@linux:/issue-6860# gcc posix_fallocate.c -o posix_fallocate
root@linux:/issue-6860# ./posix_fallocate 100
root@linux:/issue-6860# ls -l randomfile 
-rw------- 1 root root 100 Nov 13 19:44 randomfile
root@linux:/issue-6860# ./posix_fallocate 50
Bad file descriptor
root@linux:/issue-6860# ls -l randomfile 
-rw------- 1 root root 100 Nov 13 19:44 randomfile
root@linux:/issue-6860# ./posix_fallocate 10
Bad file descriptor
root@linux:/issue-6860# ls -l randomfile 
-rw------- 1 root root 100 Nov 13 19:44 randomfile
root@linux:/issue-6860# ./posix_fallocate 1000
root@linux:/issue-6860# 

I don't think there's an issue here: fallocate(2) with mode=0 on tmpfs doesn't return EOPNOTSUPP so libc is free to use the "native" syscall.

On ZFS fallocate(2) with mode=0 is not supported, so posix_fallocate(3) tries to "emulate" it: the error EBADF is actually from pread(2), because we used O_WRONLY to open the file:

root@linux:/issue-6860# strace -f ./posix_fallocate 100
execve("./posix_fallocate", ["./posix_fallocate", "100"], [/* 16 vars */]) = 0
brk(0)                                  = 0x240f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b75a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=29412, ...}) = 0
mmap(NULL, 29412, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5c2b752000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1738176, ...}) = 0
mmap(NULL, 3844640, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5c2b191000
mprotect(0x7f5c2b332000, 2097152, PROT_NONE) = 0
mmap(0x7f5c2b532000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a1000) = 0x7f5c2b532000
mmap(0x7f5c2b538000, 14880, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b538000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b751000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b750000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b74f000
arch_prctl(ARCH_SET_FS, 0x7f5c2b750700) = 0
mprotect(0x7f5c2b532000, 16384, PROT_READ) = 0
mprotect(0x7f5c2b75c000, 4096, PROT_READ) = 0
munmap(0x7f5c2b752000, 29412)           = 0
open("./randomfile", O_WRONLY|O_CREAT, 0600) = 3
fallocate(3, 0, 0, 100)                 = -1 EOPNOTSUPP (Operation not supported)
fstat(3, {st_mode=S_IFREG|0600, st_size=1000, ...}) = 0
fstatfs(3, {f_type=0x2fc12fc1, f_bsize=131072, f_blocks=191, f_bfree=191, f_bavail=191, f_files=48958, f_ffree=48949, f_fsid={1485573181, 14169277}, f_namelen=255, f_frsize=131072}) = 0
pread(3, 0x7fffc374798f, 1, 99)         = -1 EBADF (Bad file descriptor)
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c2b759000
write(1, "Bad file descriptor\n", 20Bad file descriptor
)   = 20
exit_group(0)                           = ?
+++ exited with 0 +++
root@linux:/issue-6860# 

This is documented: http://man7.org/linux/man-pages/man3/posix_fallocate.3.html#NOTES

If fd has been opened with the O_APPEND or O_WRONLY flags, the
function will fail with the error EBADF.

Change

open("./randomfile", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

to

open("./randomfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

in your reproducer to avoid this.

@CMCDragonkai
Copy link
Author

Does tmpfs supply a "native syscall" for fallocate? Ok I see the issue now.

@loli10K
Copy link
Contributor

loli10K commented Nov 14, 2017

On tmpfs we have no EOPNOTSUPP -> happy libc, no fallocate(2) "emulation" needed -> no pread(2) -> no EBADF:

root@linux:~# mount -t tmpfs tmpfs /mnt/tmpfs
root@linux:~# cp -fav posix_fallocate /mnt/tmpfs
‘posix_fallocate’ -> ‘/mnt/tmpfs/posix_fallocate’
root@linux:~# cd /mnt/tmpfs
root@linux:/mnt/tmpfs# strace -f -e trace=fallocate,pread ./posix_fallocate 100
fallocate(3, 0, 0, 100)                 = 0
+++ exited with 0 +++
root@linux:/mnt/tmpfs# strace -f -e trace=fallocate,pread ./posix_fallocate 100
fallocate(3, 0, 0, 100)                 = 0
+++ exited with 0 +++
root@linux:/mnt/tmpfs# strace -f -e trace=fallocate,pread ./posix_fallocate 50
fallocate(3, 0, 0, 50)                  = 0
+++ exited with 0 +++
root@linux:/mnt/tmpfs# strace -f -e trace=fallocate,pread ./posix_fallocate 10
fallocate(3, 0, 0, 10)                  = 0
+++ exited with 0 +++
root@linux:/mnt/tmpfs# 

@CMCDragonkai can we close this?

@CMCDragonkai
Copy link
Author

Yep

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants