LCOV - code coverage report
Current view: top level - shared - machine-pool.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 198 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 5 0.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2015 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <sys/prctl.h>
      23             : #include <sys/vfs.h>
      24             : #include <sys/statvfs.h>
      25             : #include <sys/mount.h>
      26             : 
      27             : #include "util.h"
      28             : #include "process-util.h"
      29             : #include "lockfile-util.h"
      30             : #include "mkdir.h"
      31             : #include "btrfs-util.h"
      32             : #include "path-util.h"
      33             : #include "signal-util.h"
      34             : #include "machine-pool.h"
      35             : 
      36             : #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
      37             : #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
      38             : 
      39           0 : static int check_btrfs(void) {
      40             :         struct statfs sfs;
      41             : 
      42           0 :         if (statfs("/var/lib/machines", &sfs) < 0) {
      43           0 :                 if (errno != ENOENT)
      44           0 :                         return -errno;
      45             : 
      46           0 :                 if (statfs("/var/lib", &sfs) < 0)
      47           0 :                         return -errno;
      48             :         }
      49             : 
      50           0 :         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
      51             : }
      52             : 
      53           0 : static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
      54           0 :         _cleanup_free_ char *tmp = NULL;
      55           0 :         _cleanup_close_ int fd = -1;
      56             :         struct statvfs ss;
      57           0 :         pid_t pid = 0;
      58             :         siginfo_t si;
      59             :         int r;
      60             : 
      61             :         /* We want to be able to make use of btrfs-specific file
      62             :          * system features, in particular subvolumes, reflinks and
      63             :          * quota. Hence, if we detect that /var/lib/machines.raw is
      64             :          * not located on btrfs, let's create a loopback file, place a
      65             :          * btrfs file system into it, and mount it to
      66             :          * /var/lib/machines. */
      67             : 
      68           0 :         fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
      69           0 :         if (fd >= 0) {
      70           0 :                 r = fd;
      71           0 :                 fd = -1;
      72           0 :                 return r;
      73             :         }
      74             : 
      75           0 :         if (errno != ENOENT)
      76           0 :                 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
      77             : 
      78           0 :         r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp);
      79           0 :         if (r < 0)
      80           0 :                 return r;
      81             : 
      82           0 :         (void) mkdir_p_label("/var/lib", 0755);
      83           0 :         fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
      84           0 :         if (fd < 0)
      85           0 :                 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
      86             : 
      87           0 :         if (fstatvfs(fd, &ss) < 0) {
      88           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
      89           0 :                 goto fail;
      90             :         }
      91             : 
      92           0 :         if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
      93           0 :                 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
      94           0 :                 goto fail;
      95             :         }
      96             : 
      97           0 :         if (ftruncate(fd, size) < 0) {
      98           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
      99           0 :                 goto fail;
     100             :         }
     101             : 
     102           0 :         pid = fork();
     103           0 :         if (pid < 0) {
     104           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
     105           0 :                 goto fail;
     106             :         }
     107             : 
     108           0 :         if (pid == 0) {
     109             : 
     110             :                 /* Child */
     111             : 
     112           0 :                 (void) reset_all_signal_handlers();
     113           0 :                 (void) reset_signal_mask();
     114           0 :                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
     115             : 
     116           0 :                 fd = safe_close(fd);
     117             : 
     118           0 :                 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
     119           0 :                 if (errno == ENOENT)
     120           0 :                         return 99;
     121             : 
     122           0 :                 _exit(EXIT_FAILURE);
     123             :         }
     124             : 
     125           0 :         r = wait_for_terminate(pid, &si);
     126           0 :         if (r < 0) {
     127           0 :                 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
     128           0 :                 goto fail;
     129             :         }
     130             : 
     131           0 :         pid = 0;
     132             : 
     133           0 :         if (si.si_code != CLD_EXITED) {
     134           0 :                 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
     135           0 :                 goto fail;
     136             :         }
     137           0 :         if (si.si_status == 99) {
     138           0 :                 r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
     139           0 :                 goto fail;
     140             :         }
     141           0 :         if (si.si_status != 0) {
     142           0 :                 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
     143           0 :                 goto fail;
     144             :         }
     145             : 
     146           0 :         r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
     147           0 :         if (r < 0) {
     148           0 :                 sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
     149           0 :                 goto fail;
     150             :         }
     151             : 
     152           0 :         r = fd;
     153           0 :         fd = -1;
     154             : 
     155           0 :         return r;
     156             : 
     157             : fail:
     158           0 :         unlink_noerrno(tmp);
     159             : 
     160           0 :         if (pid > 1)
     161           0 :                 kill_and_sigcont(pid, SIGKILL);
     162             : 
     163           0 :         return r;
     164             : }
     165             : 
     166           0 : int setup_machine_directory(uint64_t size, sd_bus_error *error) {
     167           0 :         _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
     168           0 :         struct loop_info64 info = {
     169             :                 .lo_flags = LO_FLAGS_AUTOCLEAR,
     170             :         };
     171           0 :         _cleanup_close_ int fd = -1, control = -1, loop = -1;
     172           0 :         _cleanup_free_ char* loopdev = NULL;
     173           0 :         char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
     174           0 :         bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
     175             :         char buf[FORMAT_BYTES_MAX];
     176           0 :         int r, nr = -1;
     177             : 
     178             :         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
     179           0 :         if (size == (uint64_t) -1)
     180           0 :                 size = VAR_LIB_MACHINES_SIZE_START;
     181           0 :         else if (size < 16*1024*1024)
     182           0 :                 size = 16*1024*1024;
     183             : 
     184             :         /* Make sure we only set the directory up once at a time */
     185           0 :         r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
     186           0 :         if (r < 0)
     187           0 :                 return r;
     188             : 
     189           0 :         r = check_btrfs();
     190           0 :         if (r < 0)
     191           0 :                 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
     192           0 :         if (r > 0) {
     193           0 :                 (void) btrfs_subvol_make_label("/var/lib/machines");
     194             : 
     195           0 :                 r = btrfs_quota_enable("/var/lib/machines", true);
     196           0 :                 if (r < 0)
     197           0 :                         log_warning_errno(r, "Failed to enable quota, ignoring: %m");
     198             : 
     199           0 :                 return 0;
     200             :         }
     201             : 
     202           0 :         if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0 ||
     203           0 :             dir_is_empty("/var/lib/machines") == 0)
     204           0 :                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
     205             : 
     206           0 :         fd = setup_machine_raw(size, error);
     207           0 :         if (fd < 0)
     208           0 :                 return fd;
     209             : 
     210           0 :         control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
     211           0 :         if (control < 0)
     212           0 :                 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
     213             : 
     214           0 :         nr = ioctl(control, LOOP_CTL_GET_FREE);
     215           0 :         if (nr < 0)
     216           0 :                 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
     217             : 
     218           0 :         if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
     219           0 :                 r = -ENOMEM;
     220           0 :                 goto fail;
     221             :         }
     222             : 
     223           0 :         loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
     224           0 :         if (loop < 0) {
     225           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
     226           0 :                 goto fail;
     227             :         }
     228             : 
     229           0 :         if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
     230           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
     231           0 :                 goto fail;
     232             :         }
     233             : 
     234           0 :         if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
     235           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
     236           0 :                 goto fail;
     237             :         }
     238             : 
     239             :         /* We need to make sure the new /var/lib/machines directory
     240             :          * has an access mode of 0700 at the time it is first made
     241             :          * available. mkfs will create it with 0755 however. Hence,
     242             :          * let's mount the directory into an inaccessible directory
     243             :          * below /tmp first, fix the access mode, and move it to the
     244             :          * public place then. */
     245             : 
     246           0 :         if (!mkdtemp(tmpdir)) {
     247           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
     248           0 :                 goto fail;
     249             :         }
     250           0 :         tmpdir_made = true;
     251             : 
     252           0 :         mntdir = strjoina(tmpdir, "/mnt");
     253           0 :         if (mkdir(mntdir, 0700) < 0) {
     254           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
     255           0 :                 goto fail;
     256             :         }
     257           0 :         mntdir_made = true;
     258             : 
     259           0 :         if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
     260           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
     261           0 :                 goto fail;
     262             :         }
     263           0 :         mntdir_mounted = true;
     264             : 
     265           0 :         r = btrfs_quota_enable(mntdir, true);
     266           0 :         if (r < 0)
     267           0 :                 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
     268             : 
     269           0 :         if (chmod(mntdir, 0700) < 0) {
     270           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
     271           0 :                 goto fail;
     272             :         }
     273             : 
     274           0 :         (void) mkdir_p_label("/var/lib/machines", 0700);
     275             : 
     276           0 :         if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
     277           0 :                 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
     278           0 :                 goto fail;
     279             :         }
     280             : 
     281           0 :         (void) syncfs(fd);
     282             : 
     283           0 :         log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
     284             : 
     285           0 :         (void) umount2(mntdir, MNT_DETACH);
     286           0 :         (void) rmdir(mntdir);
     287           0 :         (void) rmdir(tmpdir);
     288             : 
     289           0 :         return 0;
     290             : 
     291             : fail:
     292           0 :         if (mntdir_mounted)
     293           0 :                 (void) umount2(mntdir, MNT_DETACH);
     294             : 
     295           0 :         if (mntdir_made)
     296           0 :                 (void) rmdir(mntdir);
     297           0 :         if (tmpdir_made)
     298           0 :                 (void) rmdir(tmpdir);
     299             : 
     300           0 :         if (loop >= 0) {
     301           0 :                 (void) ioctl(loop, LOOP_CLR_FD);
     302           0 :                 loop = safe_close(loop);
     303             :         }
     304             : 
     305           0 :         if (control >= 0 && nr >= 0)
     306           0 :                 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
     307             : 
     308           0 :         return r;
     309             : }
     310             : 
     311           0 : static int sync_path(const char *p) {
     312           0 :         _cleanup_close_ int fd = -1;
     313             : 
     314           0 :         fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
     315           0 :         if (fd < 0)
     316           0 :                 return -errno;
     317             : 
     318           0 :         if (syncfs(fd) < 0)
     319           0 :                 return -errno;
     320             : 
     321           0 :         return 0;
     322             : }
     323             : 
     324           0 : int grow_machine_directory(void) {
     325             :         char buf[FORMAT_BYTES_MAX];
     326             :         struct statvfs a, b;
     327             :         uint64_t old_size, new_size, max_add;
     328             :         int r;
     329             : 
     330             :         /* Ensure the disk space data is accurate */
     331           0 :         sync_path("/var/lib/machines");
     332           0 :         sync_path("/var/lib/machines.raw");
     333             : 
     334           0 :         if (statvfs("/var/lib/machines.raw", &a) < 0)
     335           0 :                 return -errno;
     336             : 
     337           0 :         if (statvfs("/var/lib/machines", &b) < 0)
     338           0 :                 return -errno;
     339             : 
     340             :         /* Don't grow if not enough disk space is available on the host */
     341           0 :         if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
     342           0 :                 return 0;
     343             : 
     344             :         /* Don't grow if at least 1/3th of the fs is still free */
     345           0 :         if (b.f_bavail > b.f_blocks / 3)
     346           0 :                 return 0;
     347             : 
     348             :         /* Calculate how much we are willing to add at maximum */
     349           0 :         max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
     350             : 
     351             :         /* Calculate the old size */
     352           0 :         old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
     353             : 
     354             :         /* Calculate the new size as three times the size of what is used right now */
     355           0 :         new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
     356             : 
     357             :         /* Always, grow at least to the start size */
     358           0 :         if (new_size < VAR_LIB_MACHINES_SIZE_START)
     359           0 :                 new_size = VAR_LIB_MACHINES_SIZE_START;
     360             : 
     361             :         /* If the new size is smaller than the old size, don't grow */
     362           0 :         if (new_size < old_size)
     363           0 :                 return 0;
     364             : 
     365             :         /* Ensure we never add more than the maximum */
     366           0 :         if (new_size > old_size + max_add)
     367           0 :                 new_size = old_size + max_add;
     368             : 
     369           0 :         r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
     370           0 :         if (r <= 0)
     371           0 :                 return r;
     372             : 
     373           0 :         r = btrfs_quota_limit("/var/lib/machines", new_size);
     374           0 :         if (r < 0)
     375           0 :                 return r;
     376             : 
     377           0 :         log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
     378           0 :         return 1;
     379             : }

Generated by: LCOV version 1.11