[PATCH] libc: add fallback for posix_fallocate() when not supported by underlying FS

Olivier Blin olivier.blin at softathome.com
Mon Sep 29 14:43:32 UTC 2014


EOPNOTSUPP is not a valid error code for posix_fallocate(), the
implementation should have a fallback when the underlying filesystem
does not support fallocate().

This is especially useful when using posix_fallocate() on tmpfs with
kernels older than 3.5, for which there was no fallocate support.

This copies the implementation of the internal fallback from glibc,
with a few adaptations for internals symbols.

Though, there are no internal symbols for the following functions in
uClibc:
- fstat64
- fstatfs64 (not important, since it is not weak)
- ftruncate/ftruncate64
---
 libc/sysdeps/linux/common/posix_fallocate.c   | 78 ++++++++++++++++++++++++++-
 libc/sysdeps/linux/common/posix_fallocate64.c | 78 ++++++++++++++++++++++++++-
 2 files changed, 154 insertions(+), 2 deletions(-)

diff --git a/libc/sysdeps/linux/common/posix_fallocate.c b/libc/sysdeps/linux/common/posix_fallocate.c
index 76771e3..cc4553c 100644
--- a/libc/sysdeps/linux/common/posix_fallocate.c
+++ b/libc/sysdeps/linux/common/posix_fallocate.c
@@ -12,12 +12,88 @@
 #include <fcntl.h>
 #include <bits/kernel-features.h>
 #include <stdint.h>
+#include <sys/statfs.h>
 
 #if defined __NR_fallocate
+/* Reserve storage for the data of the file associated with FD.  */
+/* Adapted from glibc */
+int
+internal_fallocate (int fd, __off_t offset, __off_t len)
+{
+  struct stat64 st;
+  struct statfs f;
+
+  /* `off_t' is a signed type.  Therefore we can determine whether
+     OFFSET + LEN is too large if it is a negative value.  */
+  if (offset < 0 || len < 0)
+    return EINVAL;
+  if (offset + len < 0)
+    return EFBIG;
+
+  /* First thing we have to make sure is that this is really a regular
+     file.  */
+  if (fstat64 (fd, &st) != 0)
+    return EBADF;
+  if (S_ISFIFO (st.st_mode))
+    return ESPIPE;
+  if (! S_ISREG (st.st_mode))
+    return ENODEV;
+
+  if (len == 0)
+    {
+      if (st.st_size < offset)
+	{
+	  int ret = ftruncate (fd, offset);
+
+	  if (ret != 0)
+	    ret = errno;
+	  return ret;
+	}
+      return 0;
+    }
+
+  /* We have to know the block size of the filesystem to get at least some
+     sort of performance.  */
+  if (__libc_fstatfs (fd, &f) != 0)
+    return errno;
+
+  /* Try to play safe.  */
+  if (f.f_bsize == 0)
+    f.f_bsize = 512;
+
+  /* Write something to every block.  */
+  for (offset += (len - 1) % f.f_bsize; len > 0; offset += f.f_bsize)
+    {
+      len -= f.f_bsize;
+
+      if (offset < st.st_size)
+	{
+	  unsigned char c;
+	  ssize_t rsize = __libc_pread (fd, &c, 1, offset);
+
+	  if (rsize < 0)
+	    return errno;
+	  /* If there is a non-zero byte, the block must have been
+	     allocated already.  */
+	  else if (rsize == 1 && c != 0)
+	    continue;
+	}
+
+      if (__libc_pwrite (fd, "", 1, offset) != 1)
+	return errno;
+    }
+
+  return 0;
+}
+
 extern __typeof(fallocate) __libc_fallocate attribute_hidden;
 int posix_fallocate(int fd, __off_t offset, __off_t len)
 {
-	return __libc_fallocate(fd, 0, offset, len);
+	int result = __libc_fallocate(fd, 0, offset, len);
+	if (result != EOPNOTSUPP)
+		return result;
+
+	return internal_fallocate(fd, offset, len);
 }
 # if defined __UCLIBC_HAS_LFS__ && __WORDSIZE == 64
 strong_alias(posix_fallocate,posix_fallocate64)
diff --git a/libc/sysdeps/linux/common/posix_fallocate64.c b/libc/sysdeps/linux/common/posix_fallocate64.c
index 12ddbc2..2f02626 100644
--- a/libc/sysdeps/linux/common/posix_fallocate64.c
+++ b/libc/sysdeps/linux/common/posix_fallocate64.c
@@ -12,15 +12,91 @@
 #include <fcntl.h>
 #include <bits/kernel-features.h>
 #include <stdint.h>
+#include <sys/statfs.h>
 
 #if defined __NR_fallocate
 # if __WORDSIZE == 64
 /* Can use normal posix_fallocate() */
 # elif __WORDSIZE == 32
+/* Reserve storage for the data of the file associated with FD.  */
+/* Adapted from glibc */
+static int
+internal_fallocate64 (int fd, __off64_t offset, __off64_t len)
+{
+  struct stat64 st;
+  struct statfs64 f;
+
+  /* `off64_t' is a signed type.  Therefore we can determine whether
+     OFFSET + LEN is too large if it is a negative value.  */
+  if (offset < 0 || len < 0)
+    return EINVAL;
+  if (offset + len < 0)
+    return EFBIG;
+
+  /* First thing we have to make sure is that this is really a regular
+     file.  */
+  if (fstat64 (fd, &st) != 0)
+    return EBADF;
+  if (S_ISFIFO (st.st_mode))
+    return ESPIPE;
+  if (! S_ISREG (st.st_mode))
+    return ENODEV;
+
+  if (len == 0)
+    {
+      if (st.st_size < offset)
+	{
+	  int ret = ftruncate64 (fd, offset);
+
+	  if (ret != 0)
+	    ret = errno;
+	  return ret;
+	}
+      return 0;
+    }
+
+  /* We have to know the block size of the filesystem to get at least some
+     sort of performance.  */
+  if (fstatfs64 (fd, &f) != 0)
+    return errno;
+
+  /* Try to play safe.  */
+  if (f.f_bsize == 0)
+    f.f_bsize = 512;
+
+  /* Write something to every block.  */
+  for (offset += (len - 1) % f.f_bsize; len > 0; offset += f.f_bsize)
+    {
+      len -= f.f_bsize;
+
+      if (offset < st.st_size)
+	{
+	  unsigned char c;
+	  ssize_t rsize = __libc_pread64 (fd, &c, 1, offset);
+
+	  if (rsize < 0)
+	    return errno;
+	  /* If there is a non-zero byte, the block must have been
+	     allocated already.  */
+	  else if (rsize == 1 && c != 0)
+	    continue;
+	}
+
+      if (__libc_pwrite64 (fd, "", 1, offset) != 1)
+	return errno;
+    }
+
+  return 0;
+}
+
 extern __typeof(fallocate64) __libc_fallocate64 attribute_hidden;
 int posix_fallocate64(int fd, __off64_t offset, __off64_t len)
 {
-	return __libc_fallocate64(fd, 0, offset, len);
+	int result = __libc_fallocate64(fd, 0, offset, len);
+	if (result != EOPNOTSUPP)
+		return result;
+
+	return internal_fallocate64(fd, offset, len);
 }
 # else
 #  error your machine is neither 32 bit or 64 bit ... it must be magical
-- 
2.1.0

-- This message and any attachments herein are confidential, intended solely for the addressees and are SoftAtHome.s ownership. Any unauthorized use or dissemination is prohibited. If you are not the intended addressee of this message, please cancel it immediately and inform the sender.
-- This message and any attachments herein are confidential, intended solely for the addressees and are SoftAtHome.s ownership. Any unauthorized use or dissemination is prohibited. If you are not the intended addressee of this message, please cancel it immediately and inform the sender.


More information about the uClibc mailing list