#include <pakfire/private.h>
#include <pakfire/pwd.h>
#include <pakfire/repo.h>
+#include <pakfire/snapshot.h>
#include <pakfire/string.h>
#include <pakfire/transaction.h>
#include <pakfire/util.h>
// Distro
struct pakfire_distro distro;
+ // Snapshot
+ struct pakfire_snapshot* snapshot;
+
// States
unsigned int destroy_on_free:1;
unsigned int pool_ready:1;
pakfire_repo_unref(repo);
}
+ // Umount the snapshot
+ if (pakfire->snapshot)
+ pakfire_snapshot_unref(pakfire->snapshot);
+
if (pakfire->destroy_on_free && *pakfire->path) {
CTX_DEBUG(pakfire->ctx, "Destroying %s\n", pakfire->path);
if (r)
return r;
+#if 0
// Read repository configuration
r = pakfire_read_repo_config(pakfire);
if (r)
return r;
+#endif
return 0;
}
p->arches.effective = pakfire_arch_is_supported_by_host(arch);
if (!p->arches.effective) {
CTX_ERROR(p->ctx, "Unsupported architecture: %s\n", arch);
- r = errno;
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Setup user/group
+ r = pakfire_setup_user(p);
+ if (r)
+ goto ERROR;
+
+ // Initialise configuration
+ r = pakfire_config_create(&p->config);
+ if (r < 0)
+ goto ERROR;
+
+ // Read configuration file
+ r = pakfire_read_config(p, conf);
+ if (r < 0)
+ goto ERROR;
+
+ // Set cache path
+ r = pakfire_set_cache_path(p);
+ if (r < 0) {
+ CTX_ERROR(p->ctx, "Could not set cache path: %m\n");
goto ERROR;
}
goto ERROR;
}
+ // If using a snapshot, path cannot be set
+ if (p->flags & PAKFIRE_USE_SNAPSHOT) {
+ if (path) {
+ CTX_ERROR(p->ctx, "Cannot use snapshots with path\n");
+ r = -EINVAL;
+ goto ERROR;
+ }
+
+ // Find the most recent snapshot
+ r = pakfire_snapshot_find(&p->snapshot, p);
+ if (r < 0) {
+ CTX_ERROR(p->ctx, "Could not find a snapshot: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // Mount the snapshot
+ if (p->snapshot) {
+ r = pakfire_snapshot_mount(p->snapshot);
+ if (r < 0) {
+ CTX_ERROR(p->ctx, "Could not mount snapshot: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // Fetch the path where the snapshot was mounted
+ path = pakfire_snapshot_path(p->snapshot);
+ }
+ }
+
// Create a ramdisk if no path was given
if (!path) {
r = pakfire_make_ramdisk(p, tempdir, NULL);
// Set path
pakfire_string_set(p->path, path);
- // Setup user/group
- r = pakfire_setup_user(p);
- if (r)
- goto ERROR;
-
- // Initialise configuration
- r = pakfire_config_create(&p->config);
- if (r)
- goto ERROR;
-
// Read /etc/os-release
r = pakfire_read_os_release(p);
if (r && errno != ENOENT)
if (r)
goto ERROR;
- // Read configuration file
- r = pakfire_read_config(p, conf);
- if (r)
- goto ERROR;
-
// Dump distribution configuration
CTX_DEBUG(p->ctx, " Distribution: %s\n", p->distro.pretty_name);
CTX_DEBUG(p->ctx, " name = %s\n", p->distro.name);
if (*p->distro.slogan)
CTX_DEBUG(p->ctx, " slogan = %s\n", p->distro.slogan);
- // Set cache path
- r = pakfire_set_cache_path(p);
- if (r) {
- CTX_ERROR(p->ctx, "Could not set cache path: %m\n");
- goto ERROR;
- }
-
// Make path for private files
r = pakfire_path(p, private_dir, "%s", PAKFIRE_PRIVATE_DIR);
if (r)
// Format input into buffer
va_start(args, format);
- r = __pakfire_string_vformat(buffer, sizeof(buffer), format, args);
+ r = pakfire_string_vformat(buffer, format, args);
va_end(args);
// Break on any errors
- if (r)
+ if (r < 0)
return r;
// Join paths together
#############################################################################*/
#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/mount.h>
#include <archive.h>
#include <pakfire/filelist.h>
#include <pakfire/i18n.h>
#include <pakfire/logging.h>
+#include <pakfire/path.h>
#include <pakfire/private.h>
#include <pakfire/repo.h>
#include <pakfire/snapshot.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
+struct pakfire_snapshot_fs {
+ char path[PATH_MAX];
+ int fd;
+};
+
+struct pakfire_snapshot {
+ struct pakfire_ctx* ctx;
+ int nrefs;
+
+ // Path
+ char path[PATH_MAX];
+ int fd;
+
+ // File Systems
+ struct pakfire_snapshot_fs overlayfs;
+ struct pakfire_snapshot_fs tmpfs;
+
+ char upperdir[PATH_MAX];
+ char workdir[PATH_MAX];
+};
+
+static void pakfire_snapshot_free(struct pakfire_snapshot* snapshot) {
+ pakfire_snapshot_umount(snapshot);
+
+ if (snapshot->overlayfs.fd >= 0)
+ close(snapshot->overlayfs.fd);
+ if (snapshot->tmpfs.fd >= 0)
+ close(snapshot->tmpfs.fd);
+ if (snapshot->fd >= 0)
+ close(snapshot->fd);
+
+ if (snapshot->ctx)
+ pakfire_ctx_unref(snapshot->ctx);
+ free(snapshot);
+}
+
+int pakfire_snapshot_create(
+ struct pakfire_snapshot** snapshot, struct pakfire_ctx* ctx, const char* path) {
+ struct pakfire_snapshot* s = NULL;
+ int r;
+
+ // Allocate a new snapshot
+ s = calloc(1, sizeof(*s));
+ if (!s)
+ return -errno;
+
+ // Store a reference to the context
+ s->ctx = pakfire_ctx_ref(ctx);
+
+ // Initialize the reference counter
+ s->nrefs = 1;
+
+ // Store the path
+ r = pakfire_string_set(s->path, path);
+ if (r < 0)
+ goto ERROR;
+
+ // Open the directory
+ s->fd = open(path, O_DIRECTORY|O_CLOEXEC);
+ if (s->fd < 0) {
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Initialize the file descriptors
+ s->overlayfs.fd = s->tmpfs.fd = -EBADF;
+
+ // XXX Check and lock the snapshot
+
+ // Return the snapshot
+ *snapshot = pakfire_snapshot_ref(s);
+
+ERROR:
+ if (s)
+ pakfire_snapshot_unref(s);
+
+ return r;
+}
+
+struct pakfire_snapshot* pakfire_snapshot_ref(struct pakfire_snapshot* snapshot) {
+ ++snapshot->nrefs;
+
+ return snapshot;
+}
+
+struct pakfire_snapshot* pakfire_snapshot_unref(struct pakfire_snapshot* snapshot) {
+ if (--snapshot->nrefs > 0)
+ return snapshot;
+
+ pakfire_snapshot_free(snapshot);
+ return NULL;
+}
+
+static int pakfire_snapshot_filter(const struct dirent* dirent) {
+ // Skip any hidden directories
+ if (*dirent->d_name == '.')
+ return 0;
+
+ // Select the rest
+ return 1;
+}
+
+/*
+ Finds and returns the latest snapshot
+*/
+int pakfire_snapshot_find(struct pakfire_snapshot** snapshot, struct pakfire* pakfire) {
+ struct dirent** paths = NULL;
+ int num_paths = 0;
+ char path[PATH_MAX];
+ int fd = -EBADF;
+ int r;
+
+ struct pakfire_ctx* ctx = pakfire_ctx(pakfire);
+
+ // Make the path
+ r = pakfire_cache_path(pakfire, path, "%s", "snapshots");
+ if (r < 0)
+ return r;
+
+ // Open the path
+ fd = open(path, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0) {
+ switch (errno) {
+ // If the directory does not exist, we cannot find a snapshot
+ case ENOENT:
+ r = 0;
+ goto ERROR;
+
+ default:
+ r = -errno;
+ goto ERROR;
+ }
+ }
+
+ // Find all snapshots in the directory
+ num_paths = scandirat(fd, ".", &paths, pakfire_snapshot_filter, versionsort);
+ if (num_paths < 0) {
+ r = num_paths;
+ goto ERROR;
+
+ // We are done if we did not find anything
+ } else if (num_paths == 0) {
+ r = 0;
+ goto ERROR;
+ }
+
+ // List what we have found
+ for (int i = 0; i < num_paths; i++) {
+ CTX_DEBUG(ctx, "Found snapshot: %s\n", paths[i]->d_name);
+ }
+
+ // Build the path to the snapshot
+ r = pakfire_path_append(path, path, paths[num_paths - 1]->d_name);
+ if (r < 0)
+ goto ERROR;
+
+ CTX_DEBUG(ctx, "Selecting snapshot %s\n", path);
+
+ // Open the snapshot
+ r = pakfire_snapshot_create(snapshot, ctx, path);
+ if (r < 0)
+ goto ERROR;
+
+ERROR:
+ if (paths) {
+ for (int i = 0; i < num_paths; i++)
+ free(paths[i]);
+ free(paths);
+ }
+ if (fd >= 0)
+ close(fd);
+ if (ctx)
+ pakfire_ctx_unref(ctx);
+
+ return r;
+}
+
+const char* pakfire_snapshot_path(struct pakfire_snapshot* snapshot) {
+ return snapshot->overlayfs.path;
+}
+
+static int pakfire_snapshot_mount_tmpfs(struct pakfire_snapshot* snapshot) {
+ char* path = NULL;
+ int r;
+
+ // Make path
+ r = pakfire_string_set(snapshot->tmpfs.path, PAKFIRE_TMP_DIR "/pakfire-tmpfs.XXXXXX");
+ if (r < 0)
+ return r;
+
+ // Create a temporary directory
+ path = pakfire_mkdtemp(snapshot->tmpfs.path);
+ if (!path)
+ return -errno;
+
+ // Perform mount
+ r = mount("pakfire_tmpfs", snapshot->tmpfs.path, "tmpfs", 0, NULL);
+ if (r < 0)
+ return -errno;
+
+ // Make the upper directory
+ r = pakfire_path_append(snapshot->upperdir, snapshot->tmpfs.path, "upper");
+ if (r < 0)
+ return r;
+
+ // Create the upper directory
+ r = pakfire_mkdir(snapshot->upperdir, 0755);
+ if (r < 0)
+ return r;
+
+ // Make the work directory
+ r = pakfire_path_append(snapshot->workdir, snapshot->tmpfs.path, "work");
+ if (r < 0)
+ return r;
+
+ // Create the work directory
+ r = pakfire_mkdir(snapshot->workdir, 0755);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int pakfire_snapshot_mount(struct pakfire_snapshot* snapshot) {
+ const char* path = NULL;
+ int mountfd = -EBADF;
+ int fsfd = -EBADF;
+ int r;
+
+ CTX_DEBUG(snapshot->ctx, "Mounting snapshot %s\n", snapshot->path);
+
+ // Mount the tmpfs
+ r = pakfire_snapshot_mount_tmpfs(snapshot);
+ if (r < 0)
+ goto ERROR;
+
+ // Create a new overlayfs
+ fsfd = fsopen("overlay", FSOPEN_CLOEXEC);
+ if (fsfd < 0) {
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Set the lower directory
+ r = fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", snapshot->path, 0);
+ if (r < 0)
+ goto ERROR;
+
+ // Set the upper directory
+ r = fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", snapshot->upperdir, 0);
+ if (r < 0)
+ goto ERROR;
+
+ // Set some work directory
+ r = fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", snapshot->workdir, 0);
+ if (r < 0)
+ goto ERROR;
+
+ // Create a new filesystem
+ r = fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
+ if (r < 0)
+ goto ERROR;
+
+ // Perform mount
+ mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
+ if (mountfd < 0) {
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Make path
+ r = pakfire_string_set(snapshot->overlayfs.path, PAKFIRE_TMP_DIR "/pakfire-root.XXXXXX");
+ if (r < 0)
+ goto ERROR;
+
+ // Create a temporary directory
+ path = pakfire_mkdtemp(snapshot->overlayfs.path);
+ if (!path)
+ goto ERROR;
+
+ // Move the snapshot to the right place
+ r = move_mount(mountfd, "", AT_FDCWD, path, MOVE_MOUNT_F_EMPTY_PATH);
+ if (r < 0)
+ goto ERROR;
+
+ERROR:
+ if (mountfd >= 0)
+ close(mountfd);
+ if (fsfd >= 0)
+ close(fsfd);
+
+ return r;
+}
+
+int pakfire_snapshot_umount(struct pakfire_snapshot* snapshot) {
+ int r;
+
+ // Umount the overlayfs
+ if (*snapshot->overlayfs.path) {
+ r = umount(snapshot->overlayfs.path);
+ if (r < 0)
+ return r;
+ }
+
+ // Umount the tmpfs
+ if (*snapshot->tmpfs.path) {
+ r = umount(snapshot->tmpfs.path);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
#define pakfire_snapshot_path(pakfire, path) \
__pakfire_snapshot_path(pakfire, path, sizeof(path))