diff options
Diffstat (limited to 'minecctl/misc-commands.c')
-rw-r--r-- | minecctl/misc-commands.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c index 752b0e9..ff802d7 100644 --- a/minecctl/misc-commands.c +++ b/minecctl/misc-commands.c @@ -240,6 +240,9 @@ static bool valid_name(const char *name) return false; } + if (*f == '.') + return false; + while (*f != '\0') { if ((*f >= 'a' && *f <= 'z') || (*f >= 'A' && *f <= 'Z') || @@ -543,6 +546,124 @@ bool do_new(struct cfg *cfg) return true; } +static bool recursive_unlink(int dfd) +{ + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + int sdfd; + + dir = fdopendir(dfd); + if (!dir) { + error("fdopendir: %m"); + close(dfd); + return false; + } + + errno = 0; + while ((dent = readdir(dir))) { + if (streq(dent->d_name, ".") || streq(dent->d_name, "..")) + continue; + + switch (dent->d_type) { + case DT_DIR: + sdfd = openat(dfd, dent->d_name, O_RDONLY | O_CLOEXEC | + O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | + O_NOATIME); + if (sdfd < 0) { + error("Unable to open subdir %s: %m", + dent->d_name); + return false; + } + + verbose("Checking subdir %s", dent->d_name); + if (!recursive_unlink(sdfd)) + return false; + + verbose("Deleting subdir %s", dent->d_name); + if (unlinkat(dfd, dent->d_name, AT_REMOVEDIR) != 0) { + error("Unable to unlink server subdir %s: %m", + dent->d_name); + return false; + } + + break; + + case DT_LNK: + _fallthrough_; + case DT_REG: + _fallthrough_; + case DT_UNKNOWN: + verbose("Unlinking file %s", dent->d_name); + if (unlinkat(dfd, dent->d_name, 0) != 0) { + error("Failed to unlink file: %s", + dent->d_name); + return false; + } + + break; + + default: + error("Unexpected file type in server directory: %s", + dent->d_name); + return false; + } + errno = 0; + } + + if (errno != 0) { + error("readdir: %m"); + return false; + } + + return true; +} + +bool do_delete(struct cfg *cfg) +{ + const char *name = cfg->commands[0]; + char filename[strlen(name) + STRLEN(".") + STRLEN(SERVER_CONFIG_FILE_SUFFIX) + 1]; + int dfd; + + sprintf(filename, "%s.%s", name, SERVER_CONFIG_FILE_SUFFIX); + + if (!valid_name(name)) + return false; + + server_load_all_known(cfg); + + /* FIXME: Stop server first */ + + if (unlinkat(dirfd(cfg->cfg_dir), filename, 0) != 0) { + error("Unable to remove \"%s\": %m", filename); + if (!cfg->force) + return false; + } else { + verbose("Removed \"%s\"", filename); + if (!cfg->force) + return true; + } + + dfd = openat(dirfd(cfg->data_dir), name, O_RDONLY | O_CLOEXEC | + O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NOATIME); + if (dfd < 0) { + error("Unable to open server directory: %m"); + return false; + } + + if (!recursive_unlink(dfd)) { + error("Failed to remove some file(s) in server directory"); + return false; + } + + verbose("Deleting server directory %s", name); + if (unlinkat(dirfd(cfg->data_dir), name, AT_REMOVEDIR)) { + error("Failed to remove server directory: %m"); + return false; + } + + return true; +} + bool do_list(struct cfg *cfg) { struct server *server; |