diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-07-20 14:55:38 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-07-20 14:55:38 +0300 |
commit | f338532e1ae9b79c666d2ff66cc8be33ab7e5759 (patch) | |
tree | c67afd62dd2e7ac0eec28c95fc8d9af71bf0130c /src | |
parent | dd9d9c1240774e21c7eb50052225ff1e4cc376ee (diff) | |
download | wydawca-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 (limited to 'src')
-rw-r--r-- | src/backup.c | 2 | ||||
-rw-r--r-- | src/config.c | 406 | ||||
-rw-r--r-- | src/diskio.c | 5 | ||||
-rw-r--r-- | src/wydawca.c | 12 | ||||
-rw-r--r-- | src/wydawca.h | 25 |
5 files changed, 290 insertions, 160 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 |