mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-05 13:38:38 +00:00
destroy: implement in the api
This requires implementing bdev->ops->destroy() for each of the backing store types. Then implementing lxcapi_clone(), writing lxc_destroy.c using the api, and removing the lxc-destroy.in script. (this also has a few other cleanups, like marking some functions static) Changelog: fold into destroy: fix zfs destroy destroy: use correct program name in help Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
3e625e2d2e
commit
60bf62d4ae
@ -383,7 +383,6 @@ AC_CONFIG_FILES([
|
||||
src/lxc/lxc-version
|
||||
src/lxc/lxc-create
|
||||
src/lxc/lxc-start-ephemeral
|
||||
src/lxc/lxc-destroy
|
||||
src/lxc/legacy/lxc-ls
|
||||
src/lxc/lxc.functions
|
||||
|
||||
|
@ -122,8 +122,7 @@ bin_SCRIPTS = \
|
||||
lxc-netstat \
|
||||
lxc-checkconfig \
|
||||
lxc-version \
|
||||
lxc-create \
|
||||
lxc-destroy
|
||||
lxc-create
|
||||
|
||||
EXTRA_DIST = \
|
||||
lxc-device \
|
||||
@ -160,7 +159,8 @@ bin_PROGRAMS = \
|
||||
lxc-checkpoint \
|
||||
lxc-restart \
|
||||
lxc-kill \
|
||||
lxc-config
|
||||
lxc-config \
|
||||
lxc-destroy
|
||||
|
||||
pkglibexec_PROGRAMS = \
|
||||
lxc-init
|
||||
@ -179,6 +179,7 @@ lxc_cgroup_SOURCES = lxc_cgroup.c
|
||||
lxc_checkpoint_SOURCES = lxc_checkpoint.c
|
||||
lxc_config_SOURCES = lxc_config.c
|
||||
lxc_console_SOURCES = lxc_console.c
|
||||
lxc_destroy_SOURCES = lxc_destroy.c
|
||||
lxc_execute_SOURCES = lxc_execute.c
|
||||
lxc_freeze_SOURCES = lxc_freeze.c
|
||||
lxc_info_SOURCES = lxc_info.c
|
||||
|
@ -69,6 +69,9 @@ struct lxc_arguments {
|
||||
int hardstop;
|
||||
int shutdown;
|
||||
|
||||
/* for lxc-destroy */
|
||||
int force;
|
||||
|
||||
/* close fds from parent? */
|
||||
int close_all_fds;
|
||||
|
||||
|
129
src/lxc/bdev.c
129
src/lxc/bdev.c
@ -312,7 +312,7 @@ static int dir_detect(const char *path)
|
||||
//
|
||||
// XXXXXXX plain directory bind mount ops
|
||||
//
|
||||
int dir_mount(struct bdev *bdev)
|
||||
static int dir_mount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "dir"))
|
||||
return -22;
|
||||
@ -321,7 +321,7 @@ int dir_mount(struct bdev *bdev)
|
||||
return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL);
|
||||
}
|
||||
|
||||
int dir_umount(struct bdev *bdev)
|
||||
static int dir_umount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "dir"))
|
||||
return -22;
|
||||
@ -403,11 +403,19 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dir_destroy(struct bdev *orig)
|
||||
{
|
||||
if (!lxc_rmdir_onedev(orig->src))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdev_ops dir_ops = {
|
||||
.detect = &dir_detect,
|
||||
.mount = &dir_mount,
|
||||
.umount = &dir_umount,
|
||||
.clone_paths = &dir_clonepaths,
|
||||
.destroy = &dir_destroy,
|
||||
};
|
||||
|
||||
|
||||
@ -422,7 +430,7 @@ struct bdev_ops dir_ops = {
|
||||
// sake of flexibility let's always bind-mount.
|
||||
//
|
||||
|
||||
static int zfs_list_entry(const char *path, char *output)
|
||||
static int zfs_list_entry(const char *path, char *output, size_t inlen)
|
||||
{
|
||||
FILE *f;
|
||||
int found=0;
|
||||
@ -431,7 +439,7 @@ static int zfs_list_entry(const char *path, char *output)
|
||||
SYSERROR("popen failed");
|
||||
return 0;
|
||||
}
|
||||
while (fgets(output, LXC_LOG_BUFFER_SIZE, f)) {
|
||||
while (fgets(output, inlen, f)) {
|
||||
if (strstr(output, path)) {
|
||||
found = 1;
|
||||
break;
|
||||
@ -451,12 +459,12 @@ static int zfs_detect(const char *path)
|
||||
ERROR("out of memory");
|
||||
return 0;
|
||||
}
|
||||
found = zfs_list_entry(path, output);
|
||||
found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE);
|
||||
free(output);
|
||||
return found;
|
||||
}
|
||||
|
||||
int zfs_mount(struct bdev *bdev)
|
||||
static int zfs_mount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "zfs"))
|
||||
return -22;
|
||||
@ -465,7 +473,7 @@ int zfs_mount(struct bdev *bdev)
|
||||
return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL);
|
||||
}
|
||||
|
||||
int zfs_umount(struct bdev *bdev)
|
||||
static int zfs_umount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "zfs"))
|
||||
return -22;
|
||||
@ -483,7 +491,7 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
|
||||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
if (zfs_list_entry(opath, output)) {
|
||||
if (zfs_list_entry(opath, output, MAXPATHLEN)) {
|
||||
// zfsroot is output up to ' '
|
||||
if ((p = index(output, ' ')) == NULL)
|
||||
return -1;
|
||||
@ -583,11 +591,41 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
|
||||
return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: detect whether this was a clone, and if so then also delete the
|
||||
* snapshot it was based on, so that we don't hold the original
|
||||
* container busy.
|
||||
*/
|
||||
static int zfs_destroy(struct bdev *orig)
|
||||
{
|
||||
pid_t pid;
|
||||
char output[MAXPATHLEN], *p;
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
return -1;
|
||||
if (pid)
|
||||
return wait_for_pid(pid);
|
||||
|
||||
if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) {
|
||||
ERROR("Error: zfs entry for %s not found", orig->src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// zfs mount is output up to ' '
|
||||
if ((p = index(output, ' ')) == NULL)
|
||||
return -1;
|
||||
*p = '\0';
|
||||
|
||||
execlp("zfs", "zfs", "destroy", output, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct bdev_ops zfs_ops = {
|
||||
.detect = &zfs_detect,
|
||||
.mount = &zfs_mount,
|
||||
.umount = &zfs_umount,
|
||||
.clone_paths = &zfs_clonepaths,
|
||||
.destroy = &zfs_destroy,
|
||||
};
|
||||
|
||||
//
|
||||
@ -815,11 +853,25 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lvm_destroy(struct bdev *orig)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
return -1;
|
||||
if (!pid) {
|
||||
execlp("lvremove", "lvremove", "-f", orig->src, NULL);
|
||||
exit(1);
|
||||
}
|
||||
return wait_for_pid(pid);
|
||||
}
|
||||
|
||||
struct bdev_ops lvm_ops = {
|
||||
.detect = &lvm_detect,
|
||||
.mount = &lvm_mount,
|
||||
.umount = &lvm_umount,
|
||||
.clone_paths = &lvm_clonepaths,
|
||||
.destroy = &lvm_destroy,
|
||||
};
|
||||
|
||||
//
|
||||
@ -871,7 +923,7 @@ static int btrfs_detect(const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_mount(struct bdev *bdev)
|
||||
static int btrfs_mount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "btrfs"))
|
||||
return -22;
|
||||
@ -880,7 +932,7 @@ int btrfs_mount(struct bdev *bdev)
|
||||
return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL);
|
||||
}
|
||||
|
||||
int btrfs_umount(struct bdev *bdev)
|
||||
static int btrfs_umount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "btrfs"))
|
||||
return -22;
|
||||
@ -904,6 +956,8 @@ struct btrfs_ioctl_vol_args {
|
||||
struct btrfs_ioctl_vol_args_v2)
|
||||
#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
|
||||
#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
|
||||
|
||||
@ -1048,11 +1102,48 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old
|
||||
return btrfs_subvolume_create(new->dest);
|
||||
}
|
||||
|
||||
static int btrfs_destroy(struct bdev *orig)
|
||||
{
|
||||
int ret, fd = -1;
|
||||
struct btrfs_ioctl_vol_args args;
|
||||
char *path = orig->src;
|
||||
char *p, *newfull = strdup(path);
|
||||
|
||||
if (!newfull) {
|
||||
ERROR("Error: out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = rindex(newfull, '/');
|
||||
if (!p) {
|
||||
ERROR("bad path: %s", path);
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
if ((fd = open(newfull, O_RDONLY)) < 0) {
|
||||
ERROR("Error opening %s", newfull);
|
||||
free(newfull);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX);
|
||||
args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
|
||||
ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
|
||||
INFO("btrfs: snapshot create ioctl returned %d", ret);
|
||||
|
||||
free(newfull);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct bdev_ops btrfs_ops = {
|
||||
.detect = &btrfs_detect,
|
||||
.mount = &btrfs_mount,
|
||||
.umount = &btrfs_umount,
|
||||
.clone_paths = &btrfs_clonepaths,
|
||||
.destroy = &btrfs_destroy,
|
||||
};
|
||||
|
||||
//
|
||||
@ -1069,7 +1160,7 @@ static int overlayfs_detect(const char *path)
|
||||
//
|
||||
// XXXXXXX plain directory bind mount ops
|
||||
//
|
||||
int overlayfs_mount(struct bdev *bdev)
|
||||
static int overlayfs_mount(struct bdev *bdev)
|
||||
{
|
||||
char *options, *dup, *lower, *upper;
|
||||
int len;
|
||||
@ -1108,7 +1199,7 @@ int overlayfs_mount(struct bdev *bdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int overlayfs_umount(struct bdev *bdev)
|
||||
static int overlayfs_umount(struct bdev *bdev)
|
||||
{
|
||||
if (strcmp(bdev->type, "overlayfs"))
|
||||
return -22;
|
||||
@ -1217,11 +1308,25 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char
|
||||
return 0;
|
||||
}
|
||||
|
||||
int overlayfs_destroy(struct bdev *orig)
|
||||
{
|
||||
char *upper;
|
||||
|
||||
if (strncmp(orig->src, "overlayfs:", 10) != 0)
|
||||
return -22;
|
||||
upper = index(orig->src + 10, ':');
|
||||
if (!upper)
|
||||
return -22;
|
||||
upper++;
|
||||
return lxc_rmdir_onedev(upper);
|
||||
}
|
||||
|
||||
struct bdev_ops overlayfs_ops = {
|
||||
.detect = &overlayfs_detect,
|
||||
.mount = &overlayfs_mount,
|
||||
.umount = &overlayfs_umount,
|
||||
.clone_paths = &overlayfs_clonepaths,
|
||||
.destroy = &overlayfs_destroy,
|
||||
};
|
||||
|
||||
struct bdev_type bdevs[] = {
|
||||
|
@ -16,6 +16,7 @@ struct bdev_ops {
|
||||
// mount requires src and dest to be set.
|
||||
int (*mount)(struct bdev *bdev);
|
||||
int (*umount)(struct bdev *bdev);
|
||||
int (*destroy)(struct bdev *bdev);
|
||||
/* given original mount, rename the paths for cloned container */
|
||||
int (*clone_paths)(struct bdev *orig, struct bdev *new, const char *oldname,
|
||||
const char *cname, const char *oldpath, const char *lxcpath,
|
||||
|
@ -784,7 +784,7 @@ out:
|
||||
return retv;
|
||||
}
|
||||
|
||||
int recursive_rmdir(char *dirname)
|
||||
static int cgroup_rmdir(char *dirname)
|
||||
{
|
||||
struct dirent dirent, *direntp;
|
||||
DIR *dir;
|
||||
@ -817,7 +817,7 @@ int recursive_rmdir(char *dirname)
|
||||
if (ret)
|
||||
continue;
|
||||
if (S_ISDIR(mystat.st_mode))
|
||||
recursive_rmdir(pathname);
|
||||
cgroup_rmdir(pathname);
|
||||
}
|
||||
|
||||
ret = rmdir(dirname);
|
||||
@ -825,8 +825,6 @@ int recursive_rmdir(char *dirname)
|
||||
if (closedir(dir))
|
||||
ERROR("failed to close directory");
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath)
|
||||
@ -841,7 +839,7 @@ static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath)
|
||||
return -1;
|
||||
}
|
||||
DEBUG("destroying %s\n", cgname);
|
||||
if (recursive_rmdir(cgname)) {
|
||||
if (cgroup_rmdir(cgname)) {
|
||||
SYSERROR("failed to remove cgroup '%s'", cgname);
|
||||
return -1;
|
||||
}
|
||||
|
96
src/lxc/lxc_destroy.c
Normal file
96
src/lxc/lxc_destroy.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
*
|
||||
* Copyright © 2013 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||
* Copyright © 2013 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 "../lxc/lxccontainer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <lxc/lxc.h>
|
||||
#include <lxc/log.h>
|
||||
|
||||
#include "arguments.h"
|
||||
#include "utils.h"
|
||||
|
||||
lxc_log_define(lxc_destroy, lxc);
|
||||
|
||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
{
|
||||
switch (c) {
|
||||
case 'f': args->force = 1; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct option my_longopts[] = {
|
||||
{"force", no_argument, 0, 'f'},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
static struct lxc_arguments my_args = {
|
||||
.progname = "lxc-destroy",
|
||||
.help = "\
|
||||
--name=NAME [-f] [-P lxcpath]\n\
|
||||
\n\
|
||||
lxc-stop stops a container with the identifier NAME\n\
|
||||
\n\
|
||||
Options :\n\
|
||||
-n, --name=NAME NAME for name of the container\n\
|
||||
-f, --force wait for the container to shut down\n",
|
||||
.options = my_longopts,
|
||||
.parser = my_parser,
|
||||
.checker = NULL,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct lxc_container *c;
|
||||
|
||||
/* this is a short term test. We'll probably want to check for
|
||||
* write access to lxcpath instead */
|
||||
if (geteuid()) {
|
||||
fprintf(stderr, "%s must be run as root\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (lxc_arguments_parse(&my_args, argc, argv))
|
||||
exit(1);
|
||||
|
||||
if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
|
||||
my_args.progname, my_args.quiet, my_args.lxcpath[0]))
|
||||
exit(1);
|
||||
|
||||
c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
|
||||
if (!c) {
|
||||
fprintf(stderr, "System error loading container\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (c->is_running(c)) {
|
||||
if (!my_args.force) {
|
||||
fprintf(stderr, "%s is running\n", my_args.name);
|
||||
exit(1);
|
||||
}
|
||||
c->stop(c);
|
||||
}
|
||||
|
||||
exit(c->destroy(c) ? 0 : 1);
|
||||
}
|
@ -301,7 +301,7 @@ static const char *lxcapi_state(struct lxc_container *c)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_stopped_nolock(struct lxc_container *c)
|
||||
static bool is_stopped_locked(struct lxc_container *c)
|
||||
{
|
||||
lxc_state_t s;
|
||||
s = lxc_getstate(c->name, c->config_path);
|
||||
@ -1048,32 +1048,51 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *lxcapi_get_config_path(struct lxc_container *c);
|
||||
// do we want the api to support --force, or leave that to the caller?
|
||||
static bool lxcapi_destroy(struct lxc_container *c)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (!c)
|
||||
return false;
|
||||
struct bdev *r;
|
||||
bool ret = false;
|
||||
|
||||
/* container is already destroyed if we don't have a config and rootfs.path is not accessible */
|
||||
if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0))
|
||||
if (!c || !lxcapi_is_defined(c) || !c->lxc_conf || !c->lxc_conf->rootfs.path)
|
||||
return false;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
if (lxclock(c->privlock, 0))
|
||||
return false;
|
||||
if (pid == 0) { // child
|
||||
execlp("lxc-destroy", "lxc-destroy", "-n", c->name, "-P", c->config_path, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (wait_for_pid(pid) < 0) {
|
||||
ERROR("Error destroying container %s", c->name);
|
||||
if (lxclock(c->slock, 0)) {
|
||||
lxcunlock(c->privlock);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (!is_stopped_locked(c)) {
|
||||
// we should queue some sort of error - in c->error_string?
|
||||
ERROR("container %s is not stopped", c->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
|
||||
if (r) {
|
||||
if (r->ops->destroy(r) < 0) {
|
||||
ERROR("Error destroying rootfs for %s", c->name);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
const char *p1 = lxcapi_get_config_path(c);
|
||||
char *path = alloca(strlen(p1) + strlen(c->name) + 2);
|
||||
sprintf(path, "%s/%s", p1, c->name);
|
||||
if (lxc_rmdir_onedev(path) < 0) {
|
||||
ERROR("Error destroying container directory for %s", c->name);
|
||||
goto out;
|
||||
}
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
lxcunlock(c->privlock);
|
||||
lxcunlock(c->slock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v)
|
||||
@ -1203,7 +1222,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys,
|
||||
if (container_mem_lock(c))
|
||||
return false;
|
||||
|
||||
if (is_stopped_nolock(c))
|
||||
if (is_stopped_locked(c))
|
||||
goto err;
|
||||
|
||||
ret = lxc_cgroup_set(c->name, subsys, value, c->config_path);
|
||||
@ -1224,7 +1243,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c
|
||||
if (container_mem_lock(c))
|
||||
return -1;
|
||||
|
||||
if (is_stopped_nolock(c))
|
||||
if (is_stopped_locked(c))
|
||||
goto out;
|
||||
|
||||
ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path);
|
||||
|
@ -41,6 +41,81 @@
|
||||
|
||||
lxc_log_define(lxc_utils, lxc);
|
||||
|
||||
static int _recursive_rmdir_onedev(char *dirname, dev_t pdev)
|
||||
{
|
||||
struct dirent dirent, *direntp;
|
||||
DIR *dir;
|
||||
int ret, failed=0;
|
||||
char pathname[MAXPATHLEN];
|
||||
|
||||
dir = opendir(dirname);
|
||||
if (!dir) {
|
||||
ERROR("%s: failed to open %s", __func__, dirname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (!readdir_r(dir, &dirent, &direntp)) {
|
||||
struct stat mystat;
|
||||
int rc;
|
||||
|
||||
if (!direntp)
|
||||
break;
|
||||
|
||||
if (!strcmp(direntp->d_name, ".") ||
|
||||
!strcmp(direntp->d_name, ".."))
|
||||
continue;
|
||||
|
||||
rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
|
||||
if (rc < 0 || rc >= MAXPATHLEN) {
|
||||
ERROR("pathname too long");
|
||||
failed=1;
|
||||
continue;
|
||||
}
|
||||
ret = lstat(pathname, &mystat);
|
||||
if (ret) {
|
||||
ERROR("%s: failed to stat %s", __func__, pathname);
|
||||
failed=1;
|
||||
continue;
|
||||
}
|
||||
if (mystat.st_dev != pdev)
|
||||
continue;
|
||||
if (S_ISDIR(mystat.st_mode)) {
|
||||
if (!_recursive_rmdir_onedev(pathname, pdev))
|
||||
failed=1;
|
||||
} else {
|
||||
if (unlink(pathname) < 0) {
|
||||
ERROR("%s: failed to delete %s", __func__, pathname);
|
||||
failed=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rmdir(dirname) < 0) {
|
||||
ERROR("%s: failed to delete %s", __func__, dirname);
|
||||
failed=1;
|
||||
}
|
||||
|
||||
if (closedir(dir)) {
|
||||
ERROR("%s: failed to close directory %s", __func__, dirname);
|
||||
failed=1;
|
||||
}
|
||||
|
||||
return !failed;
|
||||
}
|
||||
|
||||
/* returns 1 on success, 0 if there were any failures */
|
||||
extern int lxc_rmdir_onedev(char *path)
|
||||
{
|
||||
struct stat mystat;
|
||||
|
||||
if (lstat(path, &mystat) < 0) {
|
||||
ERROR("%s: failed to stat %s", __func__, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _recursive_rmdir_onedev(path, mystat.st_dev);
|
||||
}
|
||||
|
||||
static int mount_fs(const char *source, const char *target, const char *type)
|
||||
{
|
||||
/* the umount may fail */
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <sys/types.h>
|
||||
#include "config.h"
|
||||
|
||||
/* returns 1 on success, 0 if there were any failures */
|
||||
extern int lxc_rmdir_onedev(char *path);
|
||||
extern int lxc_setup_fs(void);
|
||||
extern int get_u16(unsigned short *val, const char *arg, int base);
|
||||
extern int mkdir_p(const char *dir, mode_t mode);
|
||||
|
Loading…
Reference in New Issue
Block a user