|
|
bca718 |
commit 7fe9e2e089f4990b7d18d0798f591ab276b15f2b
|
|
|
bca718 |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
bca718 |
Date: Fri Jun 5 10:50:38 2015 +0200
|
|
|
bca718 |
|
|
|
bca718 |
posix_fallocate: Emulation fixes and documentation [BZ #15661]
|
|
|
bca718 |
|
|
|
bca718 |
Handle signed integer overflow correctly. Detect and reject O_APPEND.
|
|
|
bca718 |
Document drawbacks of emulation.
|
|
|
bca718 |
|
|
|
bca718 |
This does not completely address bug 15661, but improves the situation
|
|
|
bca718 |
somewhat.
|
|
|
bca718 |
|
|
|
bca718 |
commit 543ef578c3304661713950b37abd0c916f52ecf0
|
|
|
bca718 |
Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
bca718 |
Date: Tue Aug 25 23:42:01 2015 -0700
|
|
|
bca718 |
|
|
|
bca718 |
Fix broken overflow check in posix_fallocate [BZ 18873]
|
|
|
bca718 |
|
|
|
bca718 |
* sysdeps/posix/posix_fallocate.c (posix_fallocate):
|
|
|
bca718 |
* sysdeps/posix/posix_fallocate64.c (__posix_fallocate64_l64):
|
|
|
bca718 |
Fix parenthesization typo.
|
|
|
bca718 |
|
|
|
bca718 |
Index: b/manual/filesys.texi
|
|
|
bca718 |
===================================================================
|
|
|
bca718 |
--- a/manual/filesys.texi
|
|
|
bca718 |
+++ b/manual/filesys.texi
|
|
|
bca718 |
@@ -1723,6 +1723,7 @@ modify the attributes of a file.
|
|
|
bca718 |
access a file.
|
|
|
bca718 |
* File Times:: About the time attributes of a file.
|
|
|
bca718 |
* File Size:: Manually changing the size of a file.
|
|
|
bca718 |
+* Storage Allocation:: Allocate backing storage for files.
|
|
|
bca718 |
@end menu
|
|
|
bca718 |
|
|
|
bca718 |
@node Attribute Meanings
|
|
|
bca718 |
@@ -3232,6 +3233,99 @@ is a requirement of @code{mmap}. The pr
|
|
|
bca718 |
real size, and when it has finished a final @code{ftruncate} call should
|
|
|
bca718 |
set the real size of the file.
|
|
|
bca718 |
|
|
|
bca718 |
+@node Storage Allocation
|
|
|
bca718 |
+@subsection Storage Allocation
|
|
|
bca718 |
+@cindex allocating file storage
|
|
|
bca718 |
+@cindex file allocation
|
|
|
bca718 |
+@cindex storage allocating
|
|
|
bca718 |
+
|
|
|
bca718 |
+@cindex file fragmentation
|
|
|
bca718 |
+@cindex fragmentation of files
|
|
|
bca718 |
+@cindex sparse files
|
|
|
bca718 |
+@cindex files, sparse
|
|
|
bca718 |
+Most file systems support allocating large files in a non-contiguous
|
|
|
bca718 |
+fashion: the file is split into @emph{fragments} which are allocated
|
|
|
bca718 |
+sequentially, but the fragments themselves can be scattered across the
|
|
|
bca718 |
+disk. File systems generally try to avoid such fragmentation because it
|
|
|
bca718 |
+decreases performance, but if a file gradually increases in size, there
|
|
|
bca718 |
+might be no other option than to fragment it. In addition, many file
|
|
|
bca718 |
+systems support @emph{sparse files} with @emph{holes}: regions of null
|
|
|
bca718 |
+bytes for which no backing storage has been allocated by the file
|
|
|
bca718 |
+system. When the holes are finally overwritten with data, fragmentation
|
|
|
bca718 |
+can occur as well.
|
|
|
bca718 |
+
|
|
|
bca718 |
+Explicit allocation of storage for yet-unwritten parts of the file can
|
|
|
bca718 |
+help the system to avoid fragmentation. Additionally, if storage
|
|
|
bca718 |
+pre-allocation fails, it is possible to report the out-of-disk error
|
|
|
bca718 |
+early, often without filling up the entire disk. However, due to
|
|
|
bca718 |
+deduplication, copy-on-write semantics, and file compression, such
|
|
|
bca718 |
+pre-allocation may not reliably prevent the out-of-disk-space error from
|
|
|
bca718 |
+occurring later. Checking for write errors is still required, and
|
|
|
bca718 |
+writes to memory-mapped regions created with @code{mmap} can still
|
|
|
bca718 |
+result in @code{SIGBUS}.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@deftypefun int posix_fallocate (int @var{fd}, off_t @var{offset}, off_t @var{length})
|
|
|
bca718 |
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
|
|
bca718 |
+@c If the file system does not support allocation,
|
|
|
bca718 |
+@c @code{posix_fallocate} has a race with file extension (if
|
|
|
bca718 |
+@c @var{length} is zero) or with concurrent writes of non-NUL bytes (if
|
|
|
bca718 |
+@c @var{length} is positive).
|
|
|
bca718 |
+
|
|
|
bca718 |
+Allocate backing store for the region of @var{length} bytes starting at
|
|
|
bca718 |
+byte @var{offset} in the file for the descriptor @var{fd}. The file
|
|
|
bca718 |
+length is increased to @samp{@var{length} + @var{offset}} if necessary.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@var{fd} must be a regular file opened for writing, or @code{EBADF} is
|
|
|
bca718 |
+returned. If there is insufficient disk space to fulfill the allocation
|
|
|
bca718 |
+request, @code{ENOSPC} is returned.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@strong{Note:} If @code{fallocate} is not available (because the file
|
|
|
bca718 |
+system does not support it), @code{posix_fallocate} is emulated, which
|
|
|
bca718 |
+has the following drawbacks:
|
|
|
bca718 |
+
|
|
|
bca718 |
+@itemize @bullet
|
|
|
bca718 |
+@item
|
|
|
bca718 |
+It is very inefficient because all file system blocks in the requested
|
|
|
bca718 |
+range need to be examined (even if they have been allocated before) and
|
|
|
bca718 |
+potentially rewritten. In contrast, with proper @code{fallocate}
|
|
|
bca718 |
+support (see below), the file system can examine the internal file
|
|
|
bca718 |
+allocation data structures and eliminate holes directly, maybe even
|
|
|
bca718 |
+using unwritten extents (which are pre-allocated but uninitialized on
|
|
|
bca718 |
+disk).
|
|
|
bca718 |
+
|
|
|
bca718 |
+@item
|
|
|
bca718 |
+There is a race condition if another thread or process modifies the
|
|
|
bca718 |
+underlying file in the to-be-allocated area. Non-null bytes could be
|
|
|
bca718 |
+overwritten with null bytes.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@item
|
|
|
bca718 |
+If @var{fd} has been opened with the @code{O_APPEND} flag, the function
|
|
|
bca718 |
+will fail with an @code{errno} value of @code{EBADF}.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@item
|
|
|
bca718 |
+If @var{length} is zero, @code{ftruncate} is used to increase the file
|
|
|
bca718 |
+size as requested, without allocating file system blocks. There is a
|
|
|
bca718 |
+race condition which means that @code{ftruncate} can accidentally
|
|
|
bca718 |
+truncate the file if it has been extended concurrently.
|
|
|
bca718 |
+@end itemize
|
|
|
bca718 |
+
|
|
|
bca718 |
+On Linux, if an application does not benefit from emulation or if the
|
|
|
bca718 |
+emulation is harmful due to its inherent race conditions, the
|
|
|
bca718 |
+application can use the Linux-specific @code{fallocate} function, with a
|
|
|
bca718 |
+zero flag argument. For the @code{fallocate} function, @theglibc{} does
|
|
|
bca718 |
+not perform allocation emulation if the file system does not support
|
|
|
bca718 |
+allocation. Instead, an @code{EOPNOTSUPP} is returned to the caller.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@end deftypefun
|
|
|
bca718 |
+
|
|
|
bca718 |
+@deftypefun int posix_fallocate64 (int @var{fd}, off64_t @var{length}, off64_t @var{offset})
|
|
|
bca718 |
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
|
|
bca718 |
+
|
|
|
bca718 |
+This function is a variant of @code{posix_fallocate64} which accepts
|
|
|
bca718 |
+64-bit file offsets on all platforms.
|
|
|
bca718 |
+
|
|
|
bca718 |
+@end deftypefun
|
|
|
bca718 |
+
|
|
|
bca718 |
@node Making Special Files
|
|
|
bca718 |
@section Making Special Files
|
|
|
bca718 |
@cindex creating special files
|
|
|
bca718 |
Index: b/sysdeps/posix/posix_fallocate.c
|
|
|
bca718 |
===================================================================
|
|
|
bca718 |
--- a/sysdeps/posix/posix_fallocate.c
|
|
|
bca718 |
+++ b/sysdeps/posix/posix_fallocate.c
|
|
|
bca718 |
@@ -18,26 +18,36 @@
|
|
|
bca718 |
#include <errno.h>
|
|
|
bca718 |
#include <fcntl.h>
|
|
|
bca718 |
#include <unistd.h>
|
|
|
bca718 |
+#include <stdint.h>
|
|
|
bca718 |
+#include <sys/fcntl.h>
|
|
|
bca718 |
#include <sys/stat.h>
|
|
|
bca718 |
#include <sys/statfs.h>
|
|
|
bca718 |
|
|
|
bca718 |
-/* Reserve storage for the data of the file associated with FD. */
|
|
|
bca718 |
+/* Reserve storage for the data of the file associated with FD. This
|
|
|
bca718 |
+ emulation is far from perfect, but the kernel cannot do not much
|
|
|
bca718 |
+ better for network file systems, either. */
|
|
|
bca718 |
|
|
|
bca718 |
int
|
|
|
bca718 |
posix_fallocate (int fd, __off_t offset, __off_t len)
|
|
|
bca718 |
{
|
|
|
bca718 |
struct stat64 st;
|
|
|
bca718 |
- struct statfs f;
|
|
|
bca718 |
|
|
|
bca718 |
- /* `off_t' is a signed type. Therefore we can determine whether
|
|
|
bca718 |
- OFFSET + LEN is too large if it is a negative value. */
|
|
|
bca718 |
if (offset < 0 || len < 0)
|
|
|
bca718 |
return EINVAL;
|
|
|
bca718 |
- if (offset + len < 0)
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* Perform overflow check. The outer cast relies on a GCC
|
|
|
bca718 |
+ extension. */
|
|
|
bca718 |
+ if ((__off_t) ((uint64_t) offset + (uint64_t) len) < 0)
|
|
|
bca718 |
return EFBIG;
|
|
|
bca718 |
|
|
|
bca718 |
- /* First thing we have to make sure is that this is really a regular
|
|
|
bca718 |
- file. */
|
|
|
bca718 |
+ /* pwrite below will not do the right thing in O_APPEND mode. */
|
|
|
bca718 |
+ {
|
|
|
bca718 |
+ int flags = __fcntl (fd, F_GETFL, 0);
|
|
|
bca718 |
+ if (flags < 0 || (flags & O_APPEND) != 0)
|
|
|
bca718 |
+ return EBADF;
|
|
|
bca718 |
+ }
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* We have to make sure that this is really a regular file. */
|
|
|
bca718 |
if (__fxstat64 (_STAT_VER, fd, &st) != 0)
|
|
|
bca718 |
return EBADF;
|
|
|
bca718 |
if (S_ISFIFO (st.st_mode))
|
|
|
bca718 |
@@ -47,6 +57,8 @@ posix_fallocate (int fd, __off_t offset,
|
|
|
bca718 |
|
|
|
bca718 |
if (len == 0)
|
|
|
bca718 |
{
|
|
|
bca718 |
+ /* This is racy, but there is no good way to satisfy a
|
|
|
bca718 |
+ zero-length allocation request. */
|
|
|
bca718 |
if (st.st_size < offset)
|
|
|
bca718 |
{
|
|
|
bca718 |
int ret = __ftruncate (fd, offset);
|
|
|
bca718 |
@@ -58,19 +70,36 @@ posix_fallocate (int fd, __off_t offset,
|
|
|
bca718 |
return 0;
|
|
|
bca718 |
}
|
|
|
bca718 |
|
|
|
bca718 |
- /* We have to know the block size of the filesystem to get at least some
|
|
|
bca718 |
- sort of performance. */
|
|
|
bca718 |
- if (__fstatfs (fd, &f) != 0)
|
|
|
bca718 |
- return errno;
|
|
|
bca718 |
-
|
|
|
bca718 |
- /* Try to play safe. */
|
|
|
bca718 |
- if (f.f_bsize == 0)
|
|
|
bca718 |
- f.f_bsize = 512;
|
|
|
bca718 |
-
|
|
|
bca718 |
- /* Write something to every block. */
|
|
|
bca718 |
- for (offset += (len - 1) % f.f_bsize; len > 0; offset += f.f_bsize)
|
|
|
bca718 |
+ /* Minimize data transfer for network file systems, by issuing
|
|
|
bca718 |
+ single-byte write requests spaced by the file system block size.
|
|
|
bca718 |
+ (Most local file systems have fallocate support, so this fallback
|
|
|
bca718 |
+ code is not used there.) */
|
|
|
bca718 |
+
|
|
|
bca718 |
+ unsigned increment;
|
|
|
bca718 |
+ {
|
|
|
bca718 |
+ struct statfs64 f;
|
|
|
bca718 |
+
|
|
|
bca718 |
+ if (__fstatfs64 (fd, &f) != 0)
|
|
|
bca718 |
+ return errno;
|
|
|
bca718 |
+ if (f.f_bsize == 0)
|
|
|
bca718 |
+ increment = 512;
|
|
|
bca718 |
+ else if (f.f_bsize < 4096)
|
|
|
bca718 |
+ increment = f.f_bsize;
|
|
|
bca718 |
+ else
|
|
|
bca718 |
+ /* NFS does not propagate the block size of the underlying
|
|
|
bca718 |
+ storage and may report a much larger value which would still
|
|
|
bca718 |
+ leave holes after the loop below, so we cap the increment at
|
|
|
bca718 |
+ 4096. */
|
|
|
bca718 |
+ increment = 4096;
|
|
|
bca718 |
+ }
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* Write a null byte to every block. This is racy; we currently
|
|
|
bca718 |
+ lack a better option. Compare-and-swap against a file mapping
|
|
|
bca718 |
+ might additional local races, but requires interposition of a
|
|
|
bca718 |
+ signal handler to catch SIGBUS. */
|
|
|
bca718 |
+ for (offset += (len - 1) % increment; len > 0; offset += increment)
|
|
|
bca718 |
{
|
|
|
bca718 |
- len -= f.f_bsize;
|
|
|
bca718 |
+ len -= increment;
|
|
|
bca718 |
|
|
|
bca718 |
if (offset < st.st_size)
|
|
|
bca718 |
{
|
|
|
bca718 |
Index: b/sysdeps/posix/posix_fallocate64.c
|
|
|
bca718 |
===================================================================
|
|
|
bca718 |
--- a/sysdeps/posix/posix_fallocate64.c
|
|
|
bca718 |
+++ b/sysdeps/posix/posix_fallocate64.c
|
|
|
bca718 |
@@ -18,26 +18,36 @@
|
|
|
bca718 |
#include <errno.h>
|
|
|
bca718 |
#include <fcntl.h>
|
|
|
bca718 |
#include <unistd.h>
|
|
|
bca718 |
+#include <stdint.h>
|
|
|
bca718 |
+#include <sys/fcntl.h>
|
|
|
bca718 |
#include <sys/stat.h>
|
|
|
bca718 |
#include <sys/statfs.h>
|
|
|
bca718 |
|
|
|
bca718 |
-/* Reserve storage for the data of the file associated with FD. */
|
|
|
bca718 |
+/* Reserve storage for the data of the file associated with FD. This
|
|
|
bca718 |
+ emulation is far from perfect, but the kernel cannot do not much
|
|
|
bca718 |
+ better for network file systems, either. */
|
|
|
bca718 |
|
|
|
bca718 |
int
|
|
|
bca718 |
__posix_fallocate64_l64 (int fd, __off64_t offset, __off64_t len)
|
|
|
bca718 |
{
|
|
|
bca718 |
struct stat64 st;
|
|
|
bca718 |
- struct statfs64 f;
|
|
|
bca718 |
|
|
|
bca718 |
- /* `off64_t' is a signed type. Therefore we can determine whether
|
|
|
bca718 |
- OFFSET + LEN is too large if it is a negative value. */
|
|
|
bca718 |
if (offset < 0 || len < 0)
|
|
|
bca718 |
return EINVAL;
|
|
|
bca718 |
- if (offset + len < 0)
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* Perform overflow check. The outer cast relies on a GCC
|
|
|
bca718 |
+ extension. */
|
|
|
bca718 |
+ if ((__off64_t) ((uint64_t) offset + (uint64_t) len) < 0)
|
|
|
bca718 |
return EFBIG;
|
|
|
bca718 |
|
|
|
bca718 |
- /* First thing we have to make sure is that this is really a regular
|
|
|
bca718 |
- file. */
|
|
|
bca718 |
+ /* pwrite64 below will not do the right thing in O_APPEND mode. */
|
|
|
bca718 |
+ {
|
|
|
bca718 |
+ int flags = __fcntl (fd, F_GETFL, 0);
|
|
|
bca718 |
+ if (flags < 0 || (flags & O_APPEND) != 0)
|
|
|
bca718 |
+ return EBADF;
|
|
|
bca718 |
+ }
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* We have to make sure that this is really a regular file. */
|
|
|
bca718 |
if (__fxstat64 (_STAT_VER, fd, &st) != 0)
|
|
|
bca718 |
return EBADF;
|
|
|
bca718 |
if (S_ISFIFO (st.st_mode))
|
|
|
bca718 |
@@ -47,6 +57,8 @@ __posix_fallocate64_l64 (int fd, __off64
|
|
|
bca718 |
|
|
|
bca718 |
if (len == 0)
|
|
|
bca718 |
{
|
|
|
bca718 |
+ /* This is racy, but there is no good way to satisfy a
|
|
|
bca718 |
+ zero-length allocation request. */
|
|
|
bca718 |
if (st.st_size < offset)
|
|
|
bca718 |
{
|
|
|
bca718 |
int ret = __ftruncate64 (fd, offset);
|
|
|
bca718 |
@@ -58,19 +70,36 @@ __posix_fallocate64_l64 (int fd, __off64
|
|
|
bca718 |
return 0;
|
|
|
bca718 |
}
|
|
|
bca718 |
|
|
|
bca718 |
- /* We have to know the block size of the filesystem to get at least some
|
|
|
bca718 |
- sort of performance. */
|
|
|
bca718 |
- if (__fstatfs64 (fd, &f) != 0)
|
|
|
bca718 |
- return errno;
|
|
|
bca718 |
-
|
|
|
bca718 |
- /* Try to play safe. */
|
|
|
bca718 |
- if (f.f_bsize == 0)
|
|
|
bca718 |
- f.f_bsize = 512;
|
|
|
bca718 |
-
|
|
|
bca718 |
- /* Write something to every block. */
|
|
|
bca718 |
- for (offset += (len - 1) % f.f_bsize; len > 0; offset += f.f_bsize)
|
|
|
bca718 |
+ /* Minimize data transfer for network file systems, by issuing
|
|
|
bca718 |
+ single-byte write requests spaced by the file system block size.
|
|
|
bca718 |
+ (Most local file systems have fallocate support, so this fallback
|
|
|
bca718 |
+ code is not used there.) */
|
|
|
bca718 |
+
|
|
|
bca718 |
+ unsigned increment;
|
|
|
bca718 |
+ {
|
|
|
bca718 |
+ struct statfs64 f;
|
|
|
bca718 |
+
|
|
|
bca718 |
+ if (__fstatfs64 (fd, &f) != 0)
|
|
|
bca718 |
+ return errno;
|
|
|
bca718 |
+ if (f.f_bsize == 0)
|
|
|
bca718 |
+ increment = 512;
|
|
|
bca718 |
+ else if (f.f_bsize < 4096)
|
|
|
bca718 |
+ increment = f.f_bsize;
|
|
|
bca718 |
+ else
|
|
|
bca718 |
+ /* NFS clients do not propagate the block size of the underlying
|
|
|
bca718 |
+ storage and may report a much larger value which would still
|
|
|
bca718 |
+ leave holes after the loop below, so we cap the increment at
|
|
|
bca718 |
+ 4096. */
|
|
|
bca718 |
+ increment = 4096;
|
|
|
bca718 |
+ }
|
|
|
bca718 |
+
|
|
|
bca718 |
+ /* Write a null byte to every block. This is racy; we currently
|
|
|
bca718 |
+ lack a better option. Compare-and-swap against a file mapping
|
|
|
bca718 |
+ might address local races, but requires interposition of a signal
|
|
|
bca718 |
+ handler to catch SIGBUS. */
|
|
|
bca718 |
+ for (offset += (len - 1) % increment; len > 0; offset += increment)
|
|
|
bca718 |
{
|
|
|
bca718 |
- len -= f.f_bsize;
|
|
|
bca718 |
+ len -= increment;
|
|
|
bca718 |
|
|
|
bca718 |
if (offset < st.st_size)
|
|
|
bca718 |
{
|