summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2019-07-20 11:55:38 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2019-07-20 11:55:38 (GMT)
commitf338532e1ae9b79c666d2ff66cc8be33ab7e5759 (patch) (side-by-side diff)
treec67afd62dd2e7ac0eec28c95fc8d9af71bf0130c
parentdd9d9c1240774e21c7eb50052225ff1e4cc376ee (diff)
downloadwydawca-f338532e1ae9b79c666d2ff66cc8be33ab7e5759.tar.gz
wydawca-f338532e1ae9b79c666d2ff66cc8be33ab7e5759.tar.bz2
Create backup storage directories.
Any spool directories are actually created only if create-directories yes; appears in the configuration file. The global directory-mode and directory-owner statements provide global defaults for the directory metadata. These two keywords can also appear within a spool.archive block. * src/backup.c (split_filename): Change to extern. * src/config.c: Global directory-mode and directory-owner statements. New global statement create-directories. spool.archive.directory-mode and directory-owner statements. (cb_user,cb_supp_groups): Set wydawca_runas. (create_spool_dir): Don't mkdir unless create-directories was set. Honor wy_dry_run. Don't bail out on EPERM wheb uid is not 0. (create_spool_dirs): Compute effective metadata. Create archive directories. * src/diskio.c (create_hierarchy): Fix stack overflow if baselen==0. * src/wydawca.c (wydawca_runas): New global. (main): Intitialize wydawca_uid and wydawca_gid to current values. Run wydawca_userprivs only if wydawca_runas is set. * src/wydawca.h (archive_descr) <metadata>: New member. (wydawca_runas): New extern. * tests/etc/wydawca.rcin: Add create-directories statement. * tests/upload-dry.at: Fix expected output.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--src/backup.c2
-rw-r--r--src/config.c406
-rw-r--r--src/diskio.c5
-rw-r--r--src/wydawca.c12
-rw-r--r--src/wydawca.h25
-rw-r--r--tests/etc/wydawca.rcin2
-rw-r--r--tests/upload-dry.at3
7 files changed, 294 insertions, 161 deletions
diff --git a/src/backup.c b/src/backup.c
index 64902f7..0bf6c63 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -18,7 +18,7 @@
char const *simple_backup_suffix = "~";
-static const char *
+const char *
split_filename(char const *file, char **pdir)
{
const char *p = strrchr(file, '/');
diff --git a/src/config.c b/src/config.c
index ca0d750..3ad263d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -17,6 +17,8 @@
#include "wydawca.h"
#include "sql.h"
+static int create_directories;
+static struct directory_metadata global_directory_metadata;
struct keyword {
char *name;
@@ -598,6 +600,140 @@ static struct grecs_keyword syslog_kw[] = {
{ NULL },
};
+static int
+cb_metadata_mode(enum grecs_callback_command cmd, grecs_node_t *node,
+ void *varptr, void *cb_data)
+{
+ grecs_locus_t *locus = &node->locus;
+ grecs_value_t *value = node->v.value;
+ unsigned long m;
+ char *p;
+ struct directory_metadata *mp = varptr;
+
+ if (wy_assert_string_arg(locus, cmd, value))
+ return 1;
+ m = strtoul(value->v.string, &p, 8);
+ if (*p) {
+ grecs_error(&value->locus, 0, _("invalid file mode (near %s)"),
+ p);
+ return 1;
+ }
+ if (m & ~07777) {
+ grecs_error(&value->locus, 0, "%s",
+ _("file mode out of range"));
+ return 1;
+ }
+ mp->flags |= METADATA_MODE;
+ mp->mode = m;
+ return 0;
+}
+
+static int
+arg_to_uid(grecs_value_t *value, uid_t *uid)
+{
+ char const *user = value->v.string;
+ unsigned long n;
+ char *p;
+ struct passwd *pw;
+
+ if (user[0] == '+') {
+ user++;
+
+ errno = 0;
+ n = strtoul(user, &p, 10);
+ if (errno || *p) {
+ grecs_error(&value->locus, 0,
+ _("invalid user ID: %s"), user);
+ return 1;
+ }
+ *uid = n;
+ return 0;
+ } else if (isdigit(user[0])) {
+ errno = 0;
+ n = strtoul(user, &p, 10);
+ if (errno) {
+ grecs_error(&value->locus, 0,
+ _("invalid user ID: %s"), user);
+ return 1;
+ }
+ if (*p == 0) {
+ *uid = 0;
+ return 0;
+ }
+ }
+ pw = getpwnam(user);
+ if (!pw) {
+ grecs_error(&value->locus, 0,
+ _("no such user: %s"), user);
+ return 1;
+ }
+ *uid = pw->pw_uid;
+ return 0;
+}
+
+static int
+arg_to_gid(grecs_value_t *value, gid_t *gid)
+{
+ char const *group = value->v.string;
+ unsigned long n;
+ char *p;
+ struct group *grp;
+
+ if (group[0] == '+') {
+ group++;
+
+ errno = 0;
+ n = strtoul(group, &p, 10);
+ if (errno || *p) {
+ grecs_error(&value->locus, 0,
+ _("invalid GID: %s"), group);
+ return 1;
+ }
+ *gid = n;
+ return 0;
+ } else if (isdigit(group[0])) {
+ errno = 0;
+ n = strtoul(group, &p, 10);
+ if (errno) {
+ grecs_error(&value->locus, 0,
+ _("invalid GID: %s"), group);
+ return 1;
+ }
+ if (*p == 0) {
+ *gid = 0;
+ return 0;
+ }
+ }
+ grp = getgrnam(group);
+ if (!grp) {
+ grecs_error(&value->locus, 0,
+ _("no such group: %s"), group);
+ return 1;
+ }
+ *gid = grp->gr_gid;
+ return 0;
+}
+
+static int
+cb_metadata_owner(enum grecs_callback_command cmd, grecs_node_t *node,
+ void *varptr, void *cb_data)
+{
+ grecs_locus_t *locus = &node->locus;
+ grecs_value_t *value = node->v.value, *uval, *gval;
+ struct directory_metadata *mp = varptr;
+
+ if (!(uval = get_arg(value, 0, GRECS_TYPE_STRING)))
+ return 1;
+ if (!(gval = get_arg(value, 1, GRECS_TYPE_STRING)))
+ return 1;
+ if (arg_to_uid(uval, &mp->uid))
+ return 1;
+ if (arg_to_gid(gval, &mp->gid))
+ return 1;
+ mp->flags |= METADATA_OWNER;
+ return 0;
+}
+
static struct keyword backup_tab[] = {
{ "none", no_backups },
{ "off", no_backups },
@@ -654,6 +790,18 @@ static struct grecs_keyword archive_kw[] = {
grecs_type_string, GRECS_CONST,
NULL, offsetof(struct archive_descr, backup_type),
cb_backup },
+ { "directory-mode",
+ N_("mode: octal"),
+ N_("mode for the archive directory"),
+ grecs_type_string, GRECS_CONST,
+ NULL, offsetof(struct archive_descr, metadata),
+ cb_metadata_mode },
+ { "directory-owner",
+ N_("uid: name-or-uid> <gid: name-or-gid"),
+ N_("owner user and group for the archive directory"),
+ grecs_type_string, GRECS_CONST,
+ NULL, offsetof(struct archive_descr, metadata),
+ cb_metadata_owner },
{ NULL }
};
@@ -977,140 +1125,6 @@ cb_url(enum grecs_callback_command cmd, grecs_node_t *node,
*purl = url;
return 0;
}
-
-static int
-cb_metadata_mode(enum grecs_callback_command cmd, grecs_node_t *node,
- void *varptr, void *cb_data)
-{
- grecs_locus_t *locus = &node->locus;
- grecs_value_t *value = node->v.value;
- unsigned long m;
- char *p;
- struct directory_metadata *mp = varptr;
-
- if (wy_assert_string_arg(locus, cmd, value))
- return 1;
- m = strtoul(value->v.string, &p, 8);
- if (*p) {
- grecs_error(&value->locus, 0, _("invalid file mode (near %s)"),
- p);
- return 1;
- }
- if (m & ~07777) {
- grecs_error(&value->locus, 0, "%s",
- _("file mode out of range"));
- return 1;
- }
- mp->flags |= METADATA_MODE;
- mp->mode = m;
- return 0;
-}
-
-static int
-arg_to_uid(grecs_value_t *value, uid_t *uid)
-{
- char const *user = value->v.string;
- unsigned long n;
- char *p;
- struct passwd *pw;
-
- if (user[0] == '+') {
- user++;
-
- errno = 0;
- n = strtoul(user, &p, 10);
- if (errno || *p) {
- grecs_error(&value->locus, 0,
- _("invalid user ID: %s"), user);
- return 1;
- }
- *uid = n;
- return 0;
- } else if (isdigit(user[0])) {
- errno = 0;
- n = strtoul(user, &p, 10);
- if (errno) {
- grecs_error(&value->locus, 0,
- _("invalid user ID: %s"), user);
- return 1;
- }
- if (*p == 0) {
- *uid = 0;
- return 0;
- }
- }
- pw = getpwnam(user);
- if (!pw) {
- grecs_error(&value->locus, 0,
- _("no such user: %s"), user);
- return 1;
- }
- *uid = pw->pw_uid;
- return 0;
-}
-
-static int
-arg_to_gid(grecs_value_t *value, gid_t *gid)
-{
- char const *group = value->v.string;
- unsigned long n;
- char *p;
- struct group *grp;
-
- if (group[0] == '+') {
- group++;
-
- errno = 0;
- n = strtoul(group, &p, 10);
- if (errno || *p) {
- grecs_error(&value->locus, 0,
- _("invalid GID: %s"), group);
- return 1;
- }
- *gid = n;
- return 0;
- } else if (isdigit(group[0])) {
- errno = 0;
- n = strtoul(group, &p, 10);
- if (errno) {
- grecs_error(&value->locus, 0,
- _("invalid GID: %s"), group);
- return 1;
- }
- if (*p == 0) {
- *gid = 0;
- return 0;
- }
- }
- grp = getgrnam(group);
- if (!grp) {
- grecs_error(&value->locus, 0,
- _("no such group: %s"), group);
- return 1;
- }
- *gid = grp->gr_gid;
- return 0;
-}
-
-static int
-cb_metadata_owner(enum grecs_callback_command cmd, grecs_node_t *node,
- void *varptr, void *cb_data)
-{
- grecs_locus_t *locus = &node->locus;
- grecs_value_t *value = node->v.value, *uval, *gval;
- struct directory_metadata *mp = varptr;
-
- if (!(uval = get_arg(value, 0, GRECS_TYPE_STRING)))
- return 1;
- if (!(gval = get_arg(value, 1, GRECS_TYPE_STRING)))
- return 1;
- if (arg_to_uid(uval, &mp->uid))
- return 1;
- if (arg_to_gid(gval, &mp->gid))
- return 1;
- mp->flags |= METADATA_OWNER;
- return 0;
-}
static struct grecs_keyword spool_kw[] = {
{ "url", N_("arg"), N_("URL corresponding to this spool"),
@@ -1270,6 +1284,7 @@ cb_user(enum grecs_callback_command cmd, grecs_node_t *node,
wydawca_uid = pw->pw_uid;
wydawca_gid = pw->pw_gid;
+ wydawca_runas = 1;
return 0;
}
@@ -1317,6 +1332,7 @@ cb_supp_groups(enum grecs_callback_command cmd, grecs_node_t *node,
wydawca_supp_groups[i] = grp->gr_gid;
}
}
+ wydawca_runas = 1;
return 0;
}
@@ -1530,6 +1546,24 @@ static struct grecs_keyword wydawca_kw[] = {
{ "gpg-homedir", NULL, N_("GPG home directory"),
grecs_type_string, GRECS_CONST, &wy_gpg_homedir },
+ { "create-directories", NULL,
+ N_("Create missing directories."),
+ grecs_type_bool, GRECS_DFLT, &create_directories },
+
+ { "directory-mode",
+ N_("mode: octal"),
+ N_("mode for created directories"),
+ grecs_type_string, GRECS_CONST,
+ &global_directory_metadata, 0,
+ cb_metadata_mode },
+
+ { "directory-owner",
+ N_("uid: name-or-uid> <gid: name-or-gid"),
+ N_("owner user and group for created directory"),
+ grecs_type_string, GRECS_CONST,
+ &global_directory_metadata, 0,
+ cb_metadata_owner },
+
{NULL}
};
@@ -1570,14 +1604,24 @@ create_spool_dir(struct spool *spool, char const *dir,
{
struct stat st;
int rc;
-
+
if ((rc = stat(dir, &st)) != 0) {
if (errno != ENOENT) {
grecs_error(NULL, errno, _("%s: cannot stat %s %s"),
spool->tag, descr, dir);
return 1;
+ } else if (!create_directories) {
+ grecs_error(NULL, 0,
+ _("%s: %s %s does not exist"),
+ spool->tag, descr, dir);
+ grecs_error(NULL, 0,
+ "%s",
+ _("use \"create-directories yes\" t create it"));
+ return 1;
} else {
wy_debug(1, (_("%s: creating %s"), spool->tag, descr));
+ if (wy_dry_run)
+ return 0;
if (create_hierarchy(dir, 0)) {
return 1;
}
@@ -1588,37 +1632,111 @@ create_spool_dir(struct spool *spool, char const *dir,
return 1;
}
+ if (wy_dry_run)
+ return 0;
+
if ((meta->flags & METADATA_OWNER)
&& (rc
|| st.st_uid != meta->uid
|| st.st_gid != meta->gid)
&& chown(dir, meta->uid, meta->gid)) {
- grecs_error(NULL, errno, _("%s: can't chown %s %s"),
- spool->tag, descr, dir);
- return 1;
+ if (errno == EPERM && getuid() != 0) {
+ wy_log(LOG_WARNING,
+ _("%s: can't chown %s %s; not running as root"),
+ spool->tag, descr, dir);
+ } else {
+ grecs_error(NULL, errno, _("%s: can't chown %s %s"),
+ spool->tag, descr, dir);
+ return 1;
+ }
}
if ((meta->flags & METADATA_MODE)
&& (rc || (st.st_mode & 07777) != meta->mode)
&& chmod(dir, meta->mode)) {
- grecs_error(NULL, errno, _("%s: can't chmod %s %s"),
- spool->tag, descr, dir);
- return 1;
+ if (errno == EPERM && getuid() != 0) {
+ wy_log(LOG_WARNING,
+ _("%s: can't chmod %s %s; not running as root"),
+ spool->tag, descr, dir);
+ } else {
+ grecs_error(NULL, errno, _("%s: can't chmod %s %s"),
+ spool->tag, descr, dir);
+ return 1;
+ }
}
return 0;
}
+static struct directory_metadata *
+effective_metadata(struct directory_metadata *storage,
+ struct directory_metadata const *meta)
+{
+ if (create_directories) {
+ if (global_directory_metadata.flags) {
+ *storage = global_directory_metadata;
+ } else {
+ storage->mode = 0755;
+ storage->uid = wydawca_uid;
+ storage->gid = wydawca_gid;
+ storage->flags = METADATA_MODE|METADATA_OWNER;
+ }
+
+ if (meta->flags & METADATA_MODE) {
+ storage->mode = meta->mode;
+ storage->flags |= METADATA_MODE;
+ }
+
+ if (meta->flags & METADATA_OWNER) {
+ storage->uid = meta->uid;
+ storage->gid = meta->gid;
+ storage->flags |= METADATA_OWNER;
+ }
+ } else {
+ storage->flags = 0;
+ }
+
+ return storage;
+}
+
static int
create_spool_dirs(struct spool *spool, void *data)
{
- if (create_spool_dir(spool, spool->source_dir, &spool->source_metadata,
+ struct directory_metadata dm;
+
+ if (create_spool_dir(spool, spool->source_dir,
+ effective_metadata(&dm, &spool->source_metadata),
_("source directory")))
*(int*)data = 1;
- if (!wy_url_is_local(spool->dest_url)
+ if (wy_url_is_local(spool->dest_url)
&& create_spool_dir(spool, spool->dest_dir,
- &spool->dest_metadata,
+ effective_metadata(&dm, &spool->dest_metadata),
_("destination directory")))
*(int*)data = 1;
+
+ switch (spool->archive.type) {
+ case archive_none:
+ break;
+
+ case archive_tar: {
+ char *dir;
+ split_filename(spool->archive.name, &dir);
+ if (create_spool_dir(spool, dir,
+ effective_metadata(&dm,
+ &spool->archive.metadata),
+ _("archive directory")))
+ *(int*)data = 1;
+ free(dir);
+ }
+ break;
+
+ case archive_directory:
+ if (create_spool_dir(spool, spool->archive.name,
+ effective_metadata(&dm,
+ &spool->archive.metadata),
+ _("archive directory")))
+ *(int*)data = 1;
+ break;
+ }
return 0;
}
diff --git a/src/diskio.c b/src/diskio.c
index 6bed916..00ac097 100644
--- a/src/diskio.c
+++ b/src/diskio.c
@@ -86,9 +86,12 @@ create_hierarchy(char const *dir, size_t baselen)
return 1;
}
*p = 0;
+ rc = create_hierarchy(dir, baselen);
+ } else {
+ /* Current directory always exists */
+ rc = 0;
}
- rc = create_hierarchy(dir, baselen);
if (rc == 0) {
if (p)
*p = '/';
diff --git a/src/wydawca.c b/src/wydawca.c
index 0803aa7..4bd3620 100644
--- a/src/wydawca.c
+++ b/src/wydawca.c
@@ -23,6 +23,8 @@ uid_t wydawca_uid;
gid_t wydawca_gid;
size_t wydawca_supp_groupc;
gid_t *wydawca_supp_groups;
+int wydawca_runas; /* Set to 1 if any of the four variables above have
+ been set in the configuration */
char *conffile = SYSCONFDIR "/wydawca.rc";
const char *wy_version = "wydawca (" PACKAGE_STRING ")";
int wy_debug_level;
@@ -347,6 +349,9 @@ main(int argc, char **argv)
x_argv = argv;
parse_options(argc, argv);
+ wydawca_uid = getuid();
+ wydawca_gid = getgid();
+
argv += optind;
argc -= optind;
@@ -408,9 +413,10 @@ main(int argc, char **argv)
_("won't run with root privileges"));
exit(EX_UNAVAILABLE);
}
- } else if (wydawca_userprivs(wydawca_uid, wydawca_gid,
- wydawca_supp_groups,
- wydawca_supp_groupc))
+ } else if (wydawca_runas
+ && wydawca_userprivs(wydawca_uid, wydawca_gid,
+ wydawca_supp_groups,
+ wydawca_supp_groupc))
exit(EX_UNAVAILABLE);
}
diff --git a/src/wydawca.h b/src/wydawca.h
index cfd3ae2..581a7fe 100644
--- a/src/wydawca.h
+++ b/src/wydawca.h
@@ -118,6 +118,18 @@ enum backup_type {
extern char const *simple_backup_suffix;
char *find_backup_file_name(char const *, enum backup_type);
+const char *split_filename(char const *file, char **pdir);
+
+#define METADATA_NONE 0
+#define METADATA_MODE 0x1
+#define METADATA_OWNER 0x2
+
+struct directory_metadata {
+ int flags;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+};
/* Archive types */
@@ -135,6 +147,7 @@ struct archive_descr {
archive file name if type==archive_tar */
enum backup_type backup_type; /* Requested backup type if
type == archive_directory */
+ struct directory_metadata metadata; /* Directory metadata */
};
/* Type of file in a triplet */
@@ -216,17 +229,6 @@ struct virt_tab {
const char *file_name);
};
-#define METADATA_NONE 0
-#define METADATA_MODE 0x1
-#define METADATA_OWNER 0x2
-
-struct directory_metadata {
- int flags;
- mode_t mode;
- uid_t uid;
- gid_t gid;
-};
-
/* An upload spool. This structure contains all data necessary for releasing
files from source to destination */
struct spool {
@@ -344,6 +346,7 @@ extern uid_t wydawca_uid;
extern gid_t wydawca_gid;
extern size_t wydawca_supp_groupc;
extern gid_t *wydawca_supp_groups;
+extern int wydawca_runas;
extern char *conffile; /* Configuration file name */
extern int syslog_include_prio; /* Syslog priority indication */
extern time_t file_sweep_time; /* Unlink stale file after this amount of time
diff --git a/tests/etc/wydawca.rcin b/tests/etc/wydawca.rcin
index a655537..369ddc2 100644
--- a/tests/etc/wydawca.rcin
+++ b/tests/etc/wydawca.rcin
@@ -63,6 +63,8 @@ dictionary project-uploader {
"-----END PGP PUBLIC KEY BLOCK-----\n");
}
+create-directories yes;
+
spool test {
url ftp://wydawca.test;
source "@WY_SRC@/test";
diff --git a/tests/upload-dry.at b/tests/upload-dry.at
index 77a3212..bda0370 100644
--- a/tests/upload-dry.at
+++ b/tests/upload-dry.at
@@ -17,7 +17,8 @@
AT_SETUP([Dry-run upload])
AT_DATA([template],
-[wydawca: [[NOTICE]] AT_PACKAGE_TARNAME (AT_PACKAGE_NAME AT_PACKAGE_VERSION) started
+[wydawca: [[DEBUG]] test: creating archive directory
+wydawca: [[NOTICE]] AT_PACKAGE_TARNAME (AT_PACKAGE_NAME AT_PACKAGE_VERSION) started
wydawca: [[DEBUG]] @WY_SRC@/ok -> @WY_DST@
wydawca: [[DEBUG]] @WY_SRC@/fail -> @WY_DST@
wydawca: [[DEBUG]] @WY_SRC@/test -> @WY_DST@

Return to:

Send suggestions and report system problems to the System administrator.