daemon: Reimplement statvfs API in OCaml, drop gnulib fallback.

common/mlutils: Unix_utils.StatVFS.statvfs: This commit implements a
full-featured binding for the statvfs(3) function.

We then use this to reimplement the daemon statvfs API in OCaml.

Note that the Gnulib fallback is dropped in this commit.  It
previously referenced non-existent field names in the fs_usage struct
so it didn't work.  Also it's not necessary as POSIX has supported
statvfs(3) since 2001, it's supported in *BSD, macOS > 10.4, and there
is already a Windows fallback.
This commit is contained in:
Richard W.M. Jones
2017-10-02 17:56:51 +01:00
parent 1695a9ef30
commit bbd54295d3
14 changed files with 234 additions and 208 deletions

View File

@@ -49,7 +49,6 @@ dup3
error
filevercmp
fstatat
fsusage
fts
full-read
full-write

View File

@@ -27,7 +27,10 @@
#include <fnmatch.h>
#include <errno.h>
#include <sys/types.h>
#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if MAJOR_IN_MKDEV
#include <sys/mkdev.h>
@@ -36,6 +39,10 @@
/* else it's in sys/types.h, included above */
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/memory.h>
@@ -52,7 +59,7 @@ extern value guestfs_int_mllib_sync (value unitv);
extern value guestfs_int_mllib_fsync_file (value filenamev);
extern value guestfs_int_mllib_mkdtemp (value val_pattern);
extern value guestfs_int_mllib_realpath (value pathv);
extern value guestfs_int_mllib_statvfs_free_space (value pathv);
extern value guestfs_int_mllib_statvfs_statvfs (value pathv);
/* NB: This is a "noalloc" call. */
value
@@ -207,20 +214,111 @@ guestfs_int_mllib_realpath (value pathv)
}
value
guestfs_int_mllib_statvfs_free_space (value pathv)
guestfs_int_mllib_statvfs_statvfs (value pathv)
{
CAMLparam1 (pathv);
CAMLlocal1 (rv);
int64_t f_bsize;
int64_t f_frsize;
int64_t f_blocks;
int64_t f_bfree;
int64_t f_bavail;
int64_t f_files;
int64_t f_ffree;
int64_t f_favail;
int64_t f_fsid;
int64_t f_flag;
int64_t f_namemax;
CAMLlocal2 (rv, v);
#ifdef HAVE_STATVFS
struct statvfs buf;
int64_t free_space;
if (statvfs (String_val (pathv), &buf) == -1) {
perror ("statvfs");
caml_failwith ("statvfs");
}
if (statvfs (String_val (pathv), &buf) == -1)
unix_error (errno, (char *) "statvfs", pathv);
free_space = (int64_t) buf.f_bsize * buf.f_bavail;
rv = caml_copy_int64 (free_space);
f_bsize = buf.f_bsize;
f_frsize = buf.f_frsize;
f_blocks = buf.f_blocks;
f_bfree = buf.f_bfree;
f_bavail = buf.f_bavail;
f_files = buf.f_files;
f_ffree = buf.f_ffree;
f_favail = buf.f_favail;
f_fsid = buf.f_fsid;
f_flag = buf.f_flag;
f_namemax = buf.f_namemax;
#else /* !HAVE_STATVFS */
# if WIN32
ULONGLONG free_bytes_available; /* for user - similar to bavail */
ULONGLONG total_number_of_bytes;
ULONGLONG total_number_of_free_bytes; /* for everyone - bfree */
if (!GetDiskFreeSpaceEx (String_val (pathv),
(PULARGE_INTEGER) &free_bytes_available,
(PULARGE_INTEGER) &total_number_of_bytes,
(PULARGE_INTEGER) &total_number_of_free_bytes))
unix_error (EIO, (char *) "statvfs: GetDiskFreeSpaceEx", pathv);
/* XXX I couldn't determine how to get block size. MSDN has a
* unhelpful hard-coded list here:
* http://support.microsoft.com/kb/140365
* but this depends on the filesystem type, the size of the disk and
* the version of Windows. So this code assumes the disk is NTFS
* and the version of Windows is >= Win2K.
*/
if (total_number_of_bytes < UINT64_C (16) * 1024 * 1024 * 1024 * 1024)
f_bsize = 4096;
else if (total_number_of_bytes < UINT64_C (32) * 1024 * 1024 * 1024 * 1024)
f_bsize = 8192;
else if (total_number_of_bytes < UINT64_C (64) * 1024 * 1024 * 1024 * 1024)
f_bsize = 16384;
else if (total_number_of_bytes < UINT64_C (128) * 1024 * 1024 * 1024 * 1024)
f_bsize = 32768;
else
f_bsize = 65536;
/* As with stat, -1 indicates a field is not known. */
f_frsize = ret->bsize;
f_blocks = total_number_of_bytes / ret->bsize;
f_bfree = total_number_of_free_bytes / ret->bsize;
f_bavail = free_bytes_available / ret->bsize;
f_files = -1;
f_ffree = -1;
f_favail = -1;
f_fsid = -1;
f_flag = -1;
f_namemax = FILENAME_MAX;
# else /* !WIN32 */
#error "no statvfs or equivalent function available"
# endif /* !WIN32 */
#endif /* !HAVE_STATVFS */
/* Construct the return struct. */
rv = caml_alloc (11, 0);
v = caml_copy_int64 (f_bsize);
Store_field (rv, 0, v);
v = caml_copy_int64 (f_frsize);
Store_field (rv, 1, v);
v = caml_copy_int64 (f_blocks);
Store_field (rv, 2, v);
v = caml_copy_int64 (f_bfree);
Store_field (rv, 3, v);
v = caml_copy_int64 (f_bavail);
Store_field (rv, 4, v);
v = caml_copy_int64 (f_files);
Store_field (rv, 5, v);
v = caml_copy_int64 (f_ffree);
Store_field (rv, 6, v);
v = caml_copy_int64 (f_favail);
Store_field (rv, 7, v);
v = caml_copy_int64 (f_fsid);
Store_field (rv, 8, v);
v = caml_copy_int64 (f_flag);
Store_field (rv, 9, v);
v = caml_copy_int64 (f_namemax);
Store_field (rv, 10, v);
CAMLreturn (rv);
}

View File

@@ -16,6 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Std_utils
module Dev_t = struct
external makedev : int -> int -> int = "guestfs_int_mllib_dev_t_makedev" "noalloc"
external major : int -> int = "guestfs_int_mllib_dev_t_major" "noalloc"
@@ -61,6 +63,21 @@ module Realpath = struct
end
module StatVFS = struct
external free_space : string -> int64 =
"guestfs_int_mllib_statvfs_free_space"
type statvfs = {
f_bsize : int64;
f_frsize : int64;
f_blocks : int64;
f_bfree : int64;
f_bavail : int64;
f_files : int64;
f_ffree : int64;
f_favail : int64;
f_fsid : int64;
f_flag : int64;
f_namemax : int64;
}
external statvfs : string -> statvfs =
"guestfs_int_mllib_statvfs_statvfs"
let free_space { f_bsize = bsize; f_bavail = bavail } = bsize *^ bavail
end

View File

@@ -94,7 +94,26 @@ module Realpath : sig
end
module StatVFS : sig
val free_space : string -> int64
(** [free_space path] returns the free space available on the
type statvfs = {
f_bsize : int64; (** Filesystem block size *)
f_frsize : int64; (** Fragment size *)
f_blocks : int64; (** Size of fs in f_frsize units *)
f_bfree : int64; (** Number of free blocks *)
f_bavail : int64; (** Number of free blocks for non-root *)
f_files : int64; (** Number of inodes *)
f_ffree : int64; (** Number of free inodes *)
f_favail : int64; (** Number of free inodes for non-root *)
f_fsid : int64; (** Filesystem ID *)
f_flag : int64; (** Mount flags *)
f_namemax : int64; (** Maximum length of filenames *)
}
val statvfs : string -> statvfs
(** This calls [statvfs(3)] on the path parameter.
In case of non-Linux or non-POSIX, this call is emulated as best
we can with missing fields returned as [-1]. *)
val free_space : statvfs -> int64
(** [free_space (statvfs path)] returns the free space available on the
filesystem that contains [path], in bytes. *)
end

View File

@@ -154,7 +154,6 @@ guestfsd_SOURCES = \
sleuthkit.c \
squashfs.c \
stat.c \
statvfs.c \
strings.c \
structs-cleanups.c \
structs-cleanups.h \
@@ -274,6 +273,7 @@ SOURCES_MLI = \
optgroups.mli \
parted.mli \
realpath.mli \
statvfs.mli \
structs.mli \
sysroot.mli \
utils.mli
@@ -300,6 +300,7 @@ SOURCES_ML = \
parted.ml \
listfs.ml \
realpath.ml \
statvfs.ml \
inspect_types.ml \
inspect_utils.ml \
inspect_fs_unix_fstab.ml \
@@ -348,8 +349,10 @@ camldaemon.o: $(OBJECTS)
$(OCAMLFIND) $(BEST) -output-obj -o $@ \
$(OCAMLFLAGS) $(OCAMLPACKAGES) \
-linkpkg \
mlaugeas.$(MLARCHIVE) mlpcre.$(MLARCHIVE) \
mlcutils.$(MLARCHIVE) mlstdutils.$(MLARCHIVE) \
mlaugeas.$(MLARCHIVE) \
mlpcre.$(MLARCHIVE) \
mlstdutils.$(MLARCHIVE) \
mlcutils.$(MLARCHIVE) \
$(OBJECTS)
# OCaml dependencies.
@@ -399,8 +402,8 @@ endif
OCAMLLINKFLAGS = \
mlpcre.$(MLARCHIVE) \
mlcutils.$(MLARCHIVE) \
mlstdutils.$(MLARCHIVE) \
mlcutils.$(MLARCHIVE) \
mlaugeas.$(MLARCHIVE) \
$(LINK_CUSTOM_OCAMLC_ONLY)

View File

@@ -1,184 +0,0 @@
/* libguestfs - the guestfsd daemon
* Copyright (C) 2009 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#include <fsusage.h>
#include "guestfs_protocol.h"
#include "daemon.h"
#include "actions.h"
guestfs_int_statvfs *
do_statvfs (const char *path)
{
#ifdef HAVE_STATVFS
int r;
guestfs_int_statvfs *ret;
struct statvfs statbuf;
CHROOT_IN;
r = statvfs (path, &statbuf);
CHROOT_OUT;
if (r == -1) {
reply_with_perror ("statvfs");
return NULL;
}
ret = malloc (sizeof *ret);
if (ret == NULL) {
reply_with_perror ("malloc");
return NULL;
}
ret->bsize = statbuf.f_bsize;
ret->frsize = statbuf.f_frsize;
ret->blocks = statbuf.f_blocks;
ret->bfree = statbuf.f_bfree;
ret->bavail = statbuf.f_bavail;
ret->files = statbuf.f_files;
ret->ffree = statbuf.f_ffree;
ret->favail = statbuf.f_favail;
ret->fsid = statbuf.f_fsid;
ret->flag = statbuf.f_flag;
ret->namemax = statbuf.f_namemax;
return ret;
#else /* !HAVE_STATVFS */
# if WIN32
CLEANUP_FREE char *disk = NULL;
guestfs_int_statvfs *ret;
ULONGLONG free_bytes_available; /* for user - similar to bavail */
ULONGLONG total_number_of_bytes;
ULONGLONG total_number_of_free_bytes; /* for everyone - bfree */
disk = sysroot_path (path);
if (!disk) {
reply_with_perror ("malloc");
return NULL;
}
if (!GetDiskFreeSpaceEx (disk,
(PULARGE_INTEGER) &free_bytes_available,
(PULARGE_INTEGER) &total_number_of_bytes,
(PULARGE_INTEGER) &total_number_of_free_bytes)) {
reply_with_perror ("GetDiskFreeSpaceEx");
return NULL;
}
ret = malloc (sizeof *ret);
if (ret == NULL) {
reply_with_perror ("malloc");
return NULL;
}
/* XXX I couldn't determine how to get block size. MSDN has a
* unhelpful hard-coded list here:
* http://support.microsoft.com/kb/140365
* but this depends on the filesystem type, the size of the disk and
* the version of Windows. So this code assumes the disk is NTFS
* and the version of Windows is >= Win2K.
*/
if (total_number_of_bytes < UINT64_C (16) * 1024 * 1024 * 1024 * 1024)
ret->bsize = 4096;
else if (total_number_of_bytes < UINT64_C (32) * 1024 * 1024 * 1024 * 1024)
ret->bsize = 8192;
else if (total_number_of_bytes < UINT64_C (64) * 1024 * 1024 * 1024 * 1024)
ret->bsize = 16384;
else if (total_number_of_bytes < UINT64_C (128) * 1024 * 1024 * 1024 * 1024)
ret->bsize = 32768;
else
ret->bsize = 65536;
/* As with stat, -1 indicates a field is not known. */
ret->frsize = ret->bsize;
ret->blocks = total_number_of_bytes / ret->bsize;
ret->bfree = total_number_of_free_bytes / ret->bsize;
ret->bavail = free_bytes_available / ret->bsize;
ret->files = -1;
ret->ffree = -1;
ret->favail = -1;
ret->fsid = -1;
ret->flag = -1;
ret->namemax = FILENAME_MAX;
return ret;
# else /* !WIN32 */
CLEANUP_FREE char *disk = NULL;
int r;
guestfs_int_statvfs *ret;
struct fs_usage fsu;
disk = sysroot_path (path);
if (!disk) {
reply_with_perror ("malloc");
return NULL;
}
r = get_fs_usage (disk, disk, &fsu);
if (r == -1) {
reply_with_perror ("get_fs_usage: %s", path);
return NULL;
}
ret = malloc (sizeof *ret);
if (ret == NULL) {
reply_with_perror ("malloc");
return NULL;
}
/* As with stat, -1 indicates a field is not known. */
ret->bsize = fsu.f_bsize;
ret->frsize = -1;
ret->blocks = fsu.f_blocks;
ret->bfree = fsu.f_bfree;
ret->bavail = fsu.f_bavail;
ret->files = fsu.f_files;
ret->ffree = fsu.f_ffree;
ret->favail = -1;
ret->fsid = -1;
ret->flag = -1;
ret->namemax = -1;
return ret;
# endif /* !WIN32 */
#endif /* !HAVE_STATVFS */
}

43
daemon/statvfs.ml Normal file
View File

@@ -0,0 +1,43 @@
(* guestfs-inspection
* Copyright (C) 2009-2017 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Unix_utils
include Structs
let statvfs path =
let chroot = Chroot.create ~name:(Printf.sprintf "statvfs: %s" path) () in
let r = Chroot.f chroot StatVFS.statvfs path in
(* [r : Unix_utils.StatVFS.statvfs] is superficially similar to
* the guestfs [statvfs] structure, but not the same. We have to
* copy the fields.
*)
{
bsize = r.StatVFS.f_bsize;
frsize = r.StatVFS.f_frsize;
blocks = r.StatVFS.f_blocks;
bfree = r.StatVFS.f_bfree;
bavail = r.StatVFS.f_bavail;
files = r.StatVFS.f_files;
ffree = r.StatVFS.f_ffree;
favail = r.StatVFS.f_favail;
fsid = r.StatVFS.f_fsid;
flag = r.StatVFS.f_flag;
namemax = r.StatVFS.f_namemax;
}

33
daemon/statvfs.mli Normal file
View File

@@ -0,0 +1,33 @@
(* guestfs-inspection
* Copyright (C) 2009-2017 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
type statvfs = {
bsize : int64;
frsize : int64;
blocks : int64;
bfree : int64;
bavail : int64;
files : int64;
ffree : int64;
favail : int64;
fsid : int64;
flag : int64;
namemax : int64;
}
val statvfs : string -> statvfs

View File

@@ -166,7 +166,6 @@ daemon/sleep.c
daemon/sleuthkit.c
daemon/squashfs.c
daemon/stat.c
daemon/statvfs.c
daemon/strings.c
daemon/structs-cleanups.c
daemon/structs-cleanups.h

View File

@@ -2319,6 +2319,7 @@ See also: C<guestfs_sh_lines>" };
{ defaults with
name = "statvfs"; added = (1, 9, 2);
style = RStruct ("statbuf", "statvfs"), [String (Pathname, "path")], [];
impl = OCaml "Statvfs.statvfs";
tests = [
InitISOFS, Always, TestResult (
[["statvfs"; "/"]], "ret->namemax == 255"), []

1
m4/.gitignore vendored
View File

@@ -62,7 +62,6 @@
/fseeko.m4
/fstatat.m4
/fstat.m4
/fsusage.m4
/ftell.m4
/ftello.m4
/ftruncate.m4

View File

@@ -149,7 +149,6 @@ daemon/sleep.c
daemon/sleuthkit.c
daemon/squashfs.c
daemon/stat.c
daemon/statvfs.c
daemon/strings.c
daemon/structs-cleanups.c
daemon/stubs-0.c

View File

@@ -99,7 +99,7 @@ let run indisk outdisk check_tmpdir compress convert
virtual_size (human_size virtual_size);
let print_warning () =
let free_space = StatVFS.free_space tmpdir in
let free_space = StatVFS.free_space (StatVFS.statvfs tmpdir) in
let extra_needed = virtual_size -^ free_space in
if extra_needed > 0L then (
warning (f_"\

View File

@@ -272,7 +272,7 @@ and overlay_dir = (open_guestfs ())#get_cachedir ()
* guestfs appliance which is also stored here.
*)
and check_host_free_space () =
let free_space = StatVFS.free_space overlay_dir in
let free_space = StatVFS.free_space (StatVFS.statvfs overlay_dir) in
debug "check_host_free_space: overlay_dir=%s free_space=%Ld"
overlay_dir free_space;
if free_space < 1_073_741_824L then