diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-07-19 14:39:55 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-07-19 14:48:24 +0300 |
commit | af538cc27228edf816edea1bf52c84b5bb4f8720 (patch) | |
tree | 070e83b5149a53b84b63e707c6b2f108d911223e | |
parent | f6657a46e2422d903d62565868f77c430a612a42 (diff) | |
download | wydawca-af538cc27228edf816edea1bf52c84b5bb4f8720.tar.gz wydawca-af538cc27228edf816edea1bf52c84b5bb4f8720.tar.bz2 |
Create source directories if necessary
Missing source directories are created if at least one of the
source-mode or source-owner statements are present. If the directory
already exists, wydawca makes sure its ownership and mode matches those
requested by these statements. The syntax of the two new statements
is:
source-mode OCTAL;
source-owner USER GROUP;
USER and GROUP can be either symbolic user and group names or numeric
UID and GID. In the latter case, the number can be preceded by a
plus sign.
-rw-r--r-- | src/config.c | 220 | ||||
-rw-r--r-- | src/wydawca.h | 12 |
2 files changed, 218 insertions, 14 deletions
diff --git a/src/config.c b/src/config.c index 692dbd5..eb5f7f5 100644 --- a/src/config.c +++ b/src/config.c @@ -1,6 +1,5 @@ /* wydawca - automatic release submission daemon - Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2017, 2019 - Sergey Poznyakoff + Copyright (C) 2007-2019 Sergey Poznyakoff Wydawca is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -978,6 +977,140 @@ 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"), @@ -989,6 +1122,18 @@ static struct grecs_keyword spool_kw[] = { { "source", N_("dir"), N_("Source directory"), grecs_type_string, GRECS_CONST, NULL, offsetof(struct spool, source_dir) }, + { "source-mode", + N_("mode: octal"), + N_("mode for the source directory"), + grecs_type_string, GRECS_CONST, + NULL, offsetof(struct spool, source_metadata), + cb_metadata_mode }, + { "source-owner", + N_("uid: name-or-uid> <gid: name-or-gid"), + N_("owner user and group for the source directory"), + grecs_type_string, GRECS_CONST, + NULL, offsetof(struct spool, source_metadata), + cb_metadata_owner }, { "destination", N_("dir"), N_("Destination directory"), grecs_type_string, GRECS_CONST, NULL, offsetof(struct spool, dest_url), @@ -1043,6 +1188,7 @@ cb_spool(enum grecs_callback_command cmd, grecs_node_t *node, for (i = 0; i < NITEMS(spool->dictionary); i++) spool->dictionary[i] = default_dictionary[i]; spool->archive = default_archive_descr; + spool->source_metadata.flags = METADATA_NONE; *pdata = spool; break; @@ -1052,17 +1198,8 @@ cb_spool(enum grecs_callback_command cmd, grecs_node_t *node, if (!spool->source_dir) { grecs_error(locus, 0, _("source is not given")); rc = 1; - } else if (test_dir(spool->source_dir, &ec)) { - if (ec) - grecs_error(locus, ec, _("cannot access %s"), - spool->source_dir); - else - grecs_error(locus, 0, - _("%s is not a directory"), - spool->source_dir); - rc = 1; } - + if (!spool->dest_url) { grecs_error(locus, 0, _("destination is not given")); rc = 1; @@ -1414,11 +1551,62 @@ config_init() default_dictionary[i] = dictionary_new(i, dictionary_builtin); } +static int +create_source(struct spool *spool, void *data) +{ + struct stat st; + int rc; + + if ((rc = stat(spool->source_dir, &st)) != 0) { + if (errno != ENOENT) { + grecs_error(NULL, errno, _("%s: cannot stat %s"), + spool->tag, spool->source_dir); + *(int*)data = 1; + return 0; + } else { + wy_debug(1, (_("creating spool source directory %s"), + spool->source_dir)); + if (create_hierarchy(spool->source_dir, 0)) { + *(int*)data = 1; + return 0; + } + } + } else if (!S_ISDIR(st.st_mode)) { + grecs_error(NULL, errno, _("%s: %s is not a directory"), + spool->tag, spool->source_dir); + *(int*)data = 1; + return 0; + } + + if ((spool->source_metadata.flags & METADATA_OWNER) + && (rc + || st.st_uid != spool->source_metadata.uid + || st.st_gid != spool->source_metadata.gid) + && chown(spool->source_dir, + spool->source_metadata.uid, + spool->source_metadata.gid)) { + grecs_error(NULL, errno, _("%s: can't chown %s"), + spool->tag, spool->source_dir); + *(int*)data = 1; + return 0; + } + if ((spool->source_metadata.flags & METADATA_MODE) + && (rc || (st.st_mode & 07777) != spool->source_metadata.mode) + && chmod(spool->source_dir, spool->source_metadata.mode)) { + grecs_error(NULL, errno, _("%s: can't chmod %s"), + spool->tag, spool->source_dir); + *(int*)data = 1; + } + + return 0; +} + void config_finish(struct grecs_node *tree) { struct grecs_node *p; - + int err; + if (grecs_tree_process(tree, wydawca_kw)) exit(EX_CONFIG); for (p = tree->down; p; p = p->next) { @@ -1431,5 +1619,9 @@ config_finish(struct grecs_node *tree) grecs_error(&p->v.value->locus, 0, _("unknown module")); } - } + } + + err = 0; + if (for_each_spool(create_source, &err) || err) + exit(EX_CONFIG); } diff --git a/src/wydawca.h b/src/wydawca.h index b448904..1001694 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -215,6 +215,17 @@ 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 { @@ -222,6 +233,7 @@ struct spool { struct grecs_list *aliases; char *url; /* Download URL */ char *source_dir; /* Source directory */ + struct directory_metadata source_metadata; wy_url_t dest_url; /* Destination URL */ const char *dest_dir; /* Directory part of the above */ struct virt_tab vtab; /* Virtual method table */ |