From fdc64c5aafe435e34c4369ae8504d98ff57d7350 Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Thu, 28 May 2015 12:32:01 +0200
Subject: [PATCH] dd: add support for meta-data
Store the meta-data as plain text files in the dump directory's
sub-directory called '.libreport'.
I chose this name to allow other tools (which do not exist yet) to create
their own directories and files.
Related: #354
Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
src/include/dump_dir.h | 4 +
src/lib/dump_dir.c | 511 ++++++++++++++++++++++++++++++++++++++++++-------
tests/dump_dir.at | 171 +++++++++++++++++
3 files changed, 618 insertions(+), 68 deletions(-)
diff --git a/src/include/dump_dir.h b/src/include/dump_dir.h
index b613e0b..f33eb99 100644
--- a/src/include/dump_dir.h
+++ b/src/include/dump_dir.h
@@ -75,6 +75,10 @@ struct dump_dir {
*/
int owns_lock;
int dd_fd;
+ /* Never use this member directly, it is intialized on demand in
+ * dd_get_meta_data_dir_fd()
+ */
+ int dd_md_fd;
};
void dd_close(struct dump_dir *dd);
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
index 4dcfa66..aed2de0 100644
--- a/src/lib/dump_dir.c
+++ b/src/lib/dump_dir.c
@@ -83,6 +83,24 @@
#define RMDIR_FAIL_USLEEP (10*1000)
#define RMDIR_FAIL_COUNT 50
+// A sub-directory of a dump directory where the meta-data such as owner are
+// stored. The meta-data directory must have same owner, group and mode as its
+// parent dump directory. It is not a fatal error, if the meta-data directory
+// does not exist (backward compatibility).
+#define META_DATA_DIR_NAME ".libreport"
+
+enum {
+ /* Try to create meta-data dir if it does not exist */
+ DD_MD_GET_CREATE = 1 << 0,
+};
+
+// a little trick to copy read bits from file mode to exec bit of dir mode
+// * mode of dump directory is in the form of 640 (no X) because we create
+// files more often then we play with directories
+// * so if we want to get real mode of the directory we have to copy the read
+// bits
+#define DD_MODE_TO_DIR_MODE(mode) ((mode) | (((mode) & 0444) >> 2))
+
static char *load_text_file(const char *path, unsigned flags);
static char *load_text_file_at(int dir_fd, const char *name, unsigned flags);
@@ -112,6 +130,12 @@ static bool exist_file_dir_at(int dir_fd, const char *name)
return false;
}
+/* A valid dump dir element name is correct filename and is not a name of any
+ * internal file or directory.
+ */
+#define dd_validate_element_name(name) \
+ (str_is_correct_filename(name) && (strcmp(META_DATA_DIR_NAME, name) != 0))
+
/* Opens the file in the three following steps:
* 1. open the file with O_PATH (get a file descriptor for operations with
* inode) and O_NOFOLLOW (do not dereference symbolick links)
@@ -397,18 +421,28 @@ static inline struct dump_dir *dd_init(void)
struct dump_dir* dd = (struct dump_dir*)xzalloc(sizeof(struct dump_dir));
dd->dd_time = -1;
dd->dd_fd = -1;
+ dd->dd_md_fd = -1;
return dd;
}
int dd_exist(const struct dump_dir *dd, const char *name)
{
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot test existence. '%s' is not a valid file name", name);
const int ret = exist_file_dir_at(dd->dd_fd, name);
return ret;
}
+static void dd_close_meta_data_dir(struct dump_dir *dd)
+{
+ if (dd->dd_md_fd < 0)
+ return;
+
+ close(dd->dd_md_fd);
+ dd->dd_md_fd = -1;
+}
+
void dd_close(struct dump_dir *dd)
{
if (!dd)
@@ -419,6 +453,8 @@ void dd_close(struct dump_dir *dd)
if (dd->dd_fd >= 0)
close(dd->dd_fd);
+ dd_close_meta_data_dir(dd);
+
if (dd->next_dir)
{
closedir(dd->next_dir);
@@ -430,6 +466,255 @@ void dd_close(struct dump_dir *dd)
free(dd);
}
+static int dd_create_subdir(int dd_fd, const char *dirname, uid_t dd_uid, gid_t dd_gid, mode_t dd_mode)
+{
+ if (mkdirat(dd_fd, dirname, dd_mode) < 0)
+ {
+ perror_msg("Can't create directory '%s'", dirname);
+ return -1;
+ }
+
+ int dd_md_fd = openat(dd_fd, dirname, O_DIRECTORY | O_NOFOLLOW);
+ if (dd_md_fd < 0)
+ {
+ perror_msg("Can't open newly created directory '%s'", dirname);
+ goto fail_open;
+ }
+
+ if (dd_uid != (uid_t)-1)
+ {
+ if (fchown(dd_md_fd, dd_uid, dd_gid) != 0)
+ {
+ perror_msg("Can't change owner and group of '%s'", dirname);
+ goto fail_modify;
+ }
+ }
+
+ /* mkdir's mode (above) can be affected by umask, fix it */
+ if (fchmod(dd_md_fd, dd_mode) == -1)
+ {
+ perror_msg("Can't change mode of '%s'", dirname);
+ goto fail_modify;
+ }
+
+ return dd_md_fd;
+fail_modify:
+ close(dd_md_fd);
+fail_open:
+ if (unlinkat(dd_fd, dirname, AT_REMOVEDIR) < 0)
+ perror_msg("Fialed to unlink '%s' while cleaning up after failure", dirname);
+ return -1;
+}
+
+/* Opens the meta-data directory, checks its file system attributes and returns
+ * its file descriptor.
+ *
+ * The meta-data directory must have the same file system attributes as the
+ * parent dump directory in order to avoid unexpected situations and detects
+ * program errors (it is an error to modify bits of the dump directory and
+ * forgot to update the meta-data directory).
+ *
+ * Keep on mind that the old dump directories might miss the meta-data directory
+ * so the return value -ENOENT does not necessarily need to be fatal.
+ */
+static int dd_open_meta_data_dir(struct dump_dir *dd)
+{
+ int md_dir_fd = openat(dd->dd_fd, META_DATA_DIR_NAME, O_DIRECTORY | O_NOFOLLOW);
+ if (md_dir_fd < 0)
+ {
+ md_dir_fd = -errno;
+
+ /* ENOENT is not critical */
+ if (errno != ENOENT)
+ log_warning("Can't open meta-data '"META_DATA_DIR_NAME"'");
+ else
+ log_info("The dump dir doesn't contain '"META_DATA_DIR_NAME"'");
+
+ goto finito;
+ }
+
+ struct stat md_sb;
+ if (fstat(md_dir_fd, &md_sb) < 0)
+ {
+ log_debug("Can't stat '"META_DATA_DIR_NAME"'");
+ goto fail;
+ }
+
+ /* Test only permission bits, ignore SUID, SGID, etc. */
+ const mode_t md_mode = md_sb.st_mode & 0777;
+ const mode_t dd_mode = DD_MODE_TO_DIR_MODE(dd->mode);
+
+ if ( md_sb.st_uid != dd->dd_uid
+ || md_sb.st_gid != dd->dd_gid
+ || md_mode != dd_mode)
+ {
+ log_debug("'"META_DATA_DIR_NAME"' has different attributes than the dump dir, '%d'='%d', '%d'='%d', %o = %o",
+ md_sb.st_uid, dd->dd_uid, md_sb.st_gid, dd->dd_gid, md_mode, dd_mode);
+ goto fail;
+ }
+
+finito:
+ return md_dir_fd;
+
+fail:
+ close(md_dir_fd);
+
+ return -EINVAL;
+}
+
+/* Returns a file descriptor to the meta-data directory. Can be configured to
+ * create the directory if it does not exist.
+ *
+ * This function enables lazy initialization of the meta-data directory.
+ */
+static int dd_get_meta_data_dir_fd(struct dump_dir *dd, int flags)
+{
+ if (dd->dd_md_fd < 0)
+ {
+ dd->dd_md_fd = dd_open_meta_data_dir(dd);
+
+ if ( dd->dd_md_fd == -ENOENT
+ && (flags & DD_MD_GET_CREATE))
+ {
+ dd->dd_md_fd = dd_create_subdir(dd->dd_fd,
+ META_DATA_DIR_NAME,
+ dd->dd_uid,
+ dd->dd_gid,
+ DD_MODE_TO_DIR_MODE(dd->mode));
+ }
+ }
+
+ return dd->dd_md_fd;
+}
+
+/* A helper function useful for traversing directories.
+ *
+ * DIR* d opendir(dir_fd); ... closedir(d); closes also dir_fd but we want to
+ * keep it opened.
+ */
+static int fdreopen(int dir_fd, DIR **d)
+{
+ int opendir_fd = dup(dir_fd);
+ if (opendir_fd < 0)
+ {
+ perror_msg("dup(dir_fd)");
+ return -EBADFD;
+ }
+
+ lseek(opendir_fd, SEEK_SET, 0);
+ *d = fdopendir(opendir_fd);
+ if (!*d)
+ {
+ int ret = -errno;
+ close(opendir_fd);
+ perror_msg("fdopendir(dir_fd)");
+ return ret;
+ }
+
+ /* 'opendir_fd' will be closed with 'd' */
+ return 0;
+}
+
+/* A macro for going through the entries of a directory referenced as a file
+ * descriptor.
+ *
+ * Usage:
+ *
+ * FOREACH_REGULAR_FILE_AS_FD_AT_BEGIN(dir_fd)
+ * {
+ * printf("Short name '%s'", dent->d_name);
+ * printf("File descriptor %d", fd);
+ * }
+ * FOREACH_REGULAR_FILE_AS_FD_AT_END
+ */
+
+#define FOREACH_REGULAR_FILE_AS_FD_AT_BEGIN(dir_fd) \
+ DIR *d; \
+ struct dirent *dent; \
+ if (fdreopen(dir_fd, &d) < 0) return -1; \
+ while ((dent = readdir(d)) != NULL) \
+ { \
+ if (dot_or_dotdot(dent->d_name)) continue; \
+ int fd = secure_openat_read(dirfd(d), dent->d_name); \
+ if (fd >= 0)
+
+#define FOREACH_REGULAR_FILE_AS_FD_AT_END \
+ close(fd); \
+ } \
+ closedir(d);
+
+
+/* Sets attributes of the meta-data directory and its contents to the same
+ * attributes of the parent dump directory.
+ */
+static int dd_sanitize_mode_meta_data(struct dump_dir *dd)
+{
+ if (!dd->locked)
+ error_msg_and_die("%s: dump_dir is not opened", __func__); /* bug */
+
+ int dd_md_fd = dd_get_meta_data_dir_fd(dd, /*no create*/0);
+ if (dd_md_fd < 0)
+ return 0;
+
+ int res = fchmod(dd_md_fd, DD_MODE_TO_DIR_MODE(dd->mode));
+ if (res < 0)
+ {
+ perror_msg("Failed to chmod meta-data sub-dir");
+ return res;
+ }
+
+ FOREACH_REGULAR_FILE_AS_FD_AT_BEGIN(dd_md_fd)
+ {
+ log_debug("chmoding %s", dent->d_name);
+
+ res = fchmod(fd, dd->mode);
+ if (res)
+ {
+ perror_msg("fchmod('%s')", dent->d_name);
+ break;
+ }
+ }
+ FOREACH_REGULAR_FILE_AS_FD_AT_END
+
+ return 0;
+}
+
+/* Sets owner and group of the meta-data directory and its contents to the same
+ * attributes of the parent dump directory.
+ */
+
+static int dd_chown_meta_data(struct dump_dir *dd, uid_t uid, gid_t gid)
+{
+ if (!dd->locked)
+ error_msg_and_die("%s: dump_dir is not opened", __func__); /* bug */
+
+ int dd_md_fd = dd_get_meta_data_dir_fd(dd, /*no create*/0);
+ if (dd_md_fd < 0)
+ return 0;
+
+ int res = fchown(dd_md_fd, uid, gid);
+ if (res < 0)
+ {
+ perror_msg("Failed to chown meta-data sub-dir");
+ return res;
+ }
+
+ FOREACH_REGULAR_FILE_AS_FD_AT_BEGIN(dd_md_fd)
+ {
+ log_debug("%s: chowning %s", __func__, dent->d_name);
+
+ res = fchown(fd, uid, gid);
+ if (res)
+ {
+ perror_msg("fchown('%s')", dent->d_name);
+ break;
+ }
+ }
+ FOREACH_REGULAR_FILE_AS_FD_AT_END
+
+ return res;
+}
+
static char* rm_trailing_slashes(const char *dir)
{
unsigned len = strlen(dir);
@@ -455,45 +740,41 @@ static struct dump_dir *dd_do_open(struct dump_dir *dd, const char *dir, int fla
/* & 0666 should remove the executable bit */
dd->mode = (stat_buf.st_mode & 0666);
- dd->dd_uid = (uid_t)-1L;
- dd->dd_gid = (gid_t)-1L;
+ /* We want to have dd_uid and dd_gid always initialized. But we have to
+ * initialize it in the way which does not prevent non-privileged user
+ * from saving data in their dump directories.
+ *
+ * Non-privileged users are not allowed to change the group to
+ * 'abrt' so we have to use their GID.
+ *
+ * If the caller is super-user, we have to use dd's fs owner and fs
+ * group, because he can do everything and the data must be readable by
+ * the real owner.
+ *
+ * We always use fs uid, because non-privileged users must own the
+ * directory and super-user must use fs owner.
+ */
+ dd->dd_uid = stat_buf.st_uid;
+
+ /* We use fs group only if the caller is super-user, because we want to
+ * make sure non-privileged users can modify elements (libreport call
+ * chown(dd_uid, dd_gid) after modifying an element) and the modified
+ * elements do not have super-user's group.
+ */
+ dd->dd_gid = getegid();
if (geteuid() == 0)
- {
- /* In case caller would want to create more files, he'll need uid:gid */
- if (fstat(dd->dd_fd, &stat_buf) != 0)
- {
- error_msg("Can't stat '%s'", dd->dd_dirname);
- dd_close(dd);
- return NULL;
- }
- dd->dd_uid = stat_buf.st_uid;
dd->dd_gid = stat_buf.st_gid;
- }
if ((flags & DD_OPEN_FD_ONLY))
+ {
+ dd->dd_md_fd = dd_open_meta_data_dir(dd);
return dd;
+ }
}
errno = 0;
if (dd_lock(dd, WAIT_FOR_OTHER_PROCESS_USLEEP, flags) < 0)
{
- if ((flags & DD_OPEN_READONLY) && errno == EACCES)
- {
- /* Directory is not writable. If it seems to be readable,
- * return "read only" dd, not NULL
- *
- * Does the directory have 'r' flag?
- */
- if (faccessat(dd->dd_fd, ".", R_OK, AT_SYMLINK_NOFOLLOW) == 0)
- {
- if(dd_check(dd) != NULL)
- {
- dd_close(dd);
- dd = NULL;
- }
- return dd;
- }
- }
if (errno == EISDIR)
{
/* EISDIR: dd_lock can lock the dir, but it sees no time file there,
@@ -504,26 +785,56 @@ static struct dump_dir *dd_do_open(struct dump_dir *dd, const char *dir, int fla
* defaults to "."!
*/
error_msg("'%s' is not a problem directory", dd->dd_dirname);
+ goto fail_with_close;
}
- else
+
+ if (!(flags & DD_OPEN_READONLY))
{
- cant_access:
- if (errno == ENOENT || errno == ENOTDIR)
- {
- if (!(flags & DD_FAIL_QUIETLY_ENOENT))
- error_msg("'%s' does not exist", dd->dd_dirname);
- }
- else
- {
- if (!(flags & DD_FAIL_QUIETLY_EACCES))
- perror_msg("Can't access '%s'", dd->dd_dirname);
- }
+ log_debug("'%s' can't be opened for writing", dd->dd_dirname);
+ goto fail_with_close;
}
- dd_close(dd);
- return NULL;
+
+ if (errno != EACCES)
+ {
+ VERB3 perror_msg("failed to lock dump directory '%s'", dd->dd_dirname);
+ goto fail_with_close;
+ }
+
+ /* Directory is not writable. If it seems to be readable,
+ * return "read only" dd, not NULL
+ *
+ * Does the directory have 'r' flag?
+ */
+ if (faccessat(dd->dd_fd, ".", R_OK, AT_SYMLINK_NOFOLLOW) != 0)
+ {
+ VERB3 perror_msg("failed to lock dump directory '%s'", dd->dd_dirname);
+ goto fail_with_close;
+ }
+
+ /* dd_check prints out good log messages */
+ if(dd_check(dd) != NULL)
+ goto fail_with_close;
+
+ /* The dd is opened in READONLY moded, continue.*/
}
return dd;
+
+cant_access:
+ if (errno == ENOENT || errno == ENOTDIR)
+ {
+ if (!(flags & DD_FAIL_QUIETLY_ENOENT))
+ error_msg("'%s' does not exist", dd->dd_dirname);
+ }
+ else
+ {
+ if (!(flags & DD_FAIL_QUIETLY_EACCES))
+ perror_msg("Can't access '%s'", dd->dd_dirname);
+ }
+
+fail_with_close:
+ dd_close(dd);
+ return NULL;
}
struct dump_dir *dd_fdopendir(struct dump_dir *dd, int flags)
@@ -607,8 +918,7 @@ struct dump_dir *dd_opendir(const char *dir, int flags)
*/
struct dump_dir *dd_create_skeleton(const char *dir, uid_t uid, mode_t mode, int flags)
{
- /* a little trick to copy read bits from file mode to exec bit of dir mode*/
- mode_t dir_mode = mode | ((mode & 0444) >> 2);
+ mode_t dir_mode = DD_MODE_TO_DIR_MODE(mode);
struct dump_dir *dd = dd_init();
dd->mode = mode;
@@ -673,8 +983,25 @@ struct dump_dir *dd_create_skeleton(const char *dir, uid_t uid, mode_t mode, int
goto fail;
}
- dd->dd_uid = (uid_t)-1L;
- dd->dd_gid = (gid_t)-1L;
+ /* Initiliaze dd_uid and dd_gid to sane values which reflect the reality.
+ */
+ dd->dd_uid = stat_sb.st_uid;
+ dd->dd_gid = stat_sb.st_gid;
+
+ /* Create META-DATA directory with real fs attributes which must be changed
+ * in dd_reset_ownership(), when populating of a new dump directory is
+ * done.
+ *
+ * It allows daemons to create a dump directory, populate the directory as
+ * root and then switch the ownership to the real user.
+ */
+ dd->dd_md_fd = dd_create_subdir(dd->dd_fd, META_DATA_DIR_NAME, dd->dd_uid, dd->dd_gid, dir_mode);
+ if (dd->dd_md_fd < 0)
+ {
+ error_msg("Can't create meta-data directory");
+ goto fail;
+ }
+
if (uid != (uid_t)-1L)
{
dd->dd_uid = 0;
@@ -726,12 +1053,16 @@ int dd_reset_ownership(struct dump_dir *dd)
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- const int r = fchown(dd->dd_fd, dd->dd_uid, dd->dd_gid);
+ int r = fchown(dd->dd_fd, dd->dd_uid, dd->dd_gid);
if (r < 0)
{
perror_msg("Can't change '%s' ownership to %lu:%lu", dd->dd_dirname,
(long)dd->dd_uid, (long)dd->dd_gid);
}
+
+ if (dd_chown_meta_data(dd, dd->dd_uid, dd->dd_gid) != 0)
+ error_msg("Failed to reset ownership of meta-data");
+
return r;
}
@@ -864,27 +1195,27 @@ void dd_sanitize_mode_and_owner(struct dump_dir *dd)
next:
free(short_name);
}
+
+ /* No need to check return value, the functions print good messages.
+ * There are two approaches for handling errors in libreport:
+ * - print out a warning message and keep status quo
+ * - terminate the process
+ */
+ dd_sanitize_mode_meta_data(dd);
+ dd_chown_meta_data(dd, dd->dd_uid, dd->dd_gid);
}
static int delete_file_dir(int dir_fd, bool skip_lock_file)
{
- int opendir_fd = dup(dir_fd);
- if (opendir_fd < 0)
+ DIR *d;
+ int ret = fdreopen(dir_fd, &d);
+ if (ret < 0)
{
- perror_msg("delete_file_dir: dup(dir_fd)");
- return -1;
- }
-
- lseek(opendir_fd, SEEK_SET, 0);
- DIR *d = fdopendir(opendir_fd);
- if (!d)
- {
- close(opendir_fd);
/* The caller expects us to error out only if the directory
* still exists (not deleted). If directory
* *doesn't exist*, return 0 and clear errno.
*/
- if (errno == ENOENT || errno == ENOTDIR)
+ if (ret == -ENOENT || ret == -ENOTDIR)
{
errno = 0;
return 0;
@@ -946,6 +1277,35 @@ static int delete_file_dir(int dir_fd, bool skip_lock_file)
return 0;
}
+static int dd_delete_meta_data(struct dump_dir *dd)
+{
+ if (!dd->locked)
+ {
+ error_msg("Can't remove meta-data of unlocked problem directory %s", dd->dd_dirname);
+ return -1;
+ }
+
+ int dd_md_fd = dd_get_meta_data_dir_fd(dd, /*no create*/0);
+ if (dd_md_fd < 0)
+ return 0;
+
+ if (delete_file_dir(dd_md_fd, /*skip_lock_file:*/ true) != 0)
+ {
+ perror_msg("Can't remove meta-data from '"META_DATA_DIR_NAME"'");
+ return -2;
+ }
+
+ dd_close_meta_data_dir(dd);
+
+ if (unlinkat(dd->dd_fd, META_DATA_DIR_NAME, AT_REMOVEDIR))
+ {
+ perror_msg("Can't remove meta-data directory '"META_DATA_DIR_NAME"'");
+ return -3;
+ }
+
+ return 0;
+}
+
int dd_delete(struct dump_dir *dd)
{
if (!dd->locked)
@@ -954,6 +1314,9 @@ int dd_delete(struct dump_dir *dd)
return -1;
}
+ if (dd_delete_meta_data(dd) != 0)
+ return -2;
+
if (delete_file_dir(dd->dd_fd, /*skip_lock_file:*/ true) != 0)
{
perror_msg("Can't remove contents of directory '%s'", dd->dd_dirname);
@@ -1029,7 +1392,10 @@ int dd_chown(struct dump_dir *dd, uid_t new_uid)
chown_res = fchown(fd, owners_uid, groups_gid);
if (chown_res)
+ {
perror_msg("fchownat('%s')", short_name);
+ break;
+ }
close(fd);
next:
@@ -1037,6 +1403,15 @@ next:
}
}
+ if (chown_res == 0)
+ chown_res = dd_chown_meta_data(dd, owners_uid, groups_gid);
+
+ if (chown_res == 0)
+ {
+ dd->dd_uid = owners_uid;
+ dd->dd_gid = groups_gid;
+ }
+
return chown_res;
}
@@ -1175,7 +1550,7 @@ char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned fla
// if (!dd->locked)
// error_msg_and_die("dump_dir is not opened"); /* bug */
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
{
error_msg("Cannot load text. '%s' is not a valid file name", name);
if ((flags & DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE))
@@ -1201,7 +1576,7 @@ void dd_save_text(struct dump_dir *dd, const char *name, const char *data)
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot save text. '%s' is not a valid file name", name);
save_binary_file_at(dd->dd_fd, name, data, strlen(data), dd->dd_uid, dd->dd_gid, dd->mode);
@@ -1212,7 +1587,7 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot save binary. '%s' is not a valid file name", name);
save_binary_file_at(dd->dd_fd, name, data, size, dd->dd_uid, dd->dd_gid, dd->mode);
@@ -1220,7 +1595,7 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns
long dd_get_item_size(struct dump_dir *dd, const char *name)
{
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot get item size. '%s' is not a valid file name", name);
long size = -1;
@@ -1245,7 +1620,7 @@ int dd_delete_item(struct dump_dir *dd, const char *name)
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot delete item. '%s' is not a valid file name", name);
int res = unlinkat(dd->dd_fd, name, /*only files*/0);
@@ -1483,7 +1858,7 @@ int dd_mark_as_notreportable(struct dump_dir *dd, const char *reason)
int dd_copy_file(struct dump_dir *dd, const char *name, const char *source_path)
{
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot test existence. '%s' is not a valid file name", name);
log_debug("copying '%s' to '%s' at '%s'", source_path, name, dd->dd_dirname);
@@ -1502,7 +1877,7 @@ int dd_copy_file(struct dump_dir *dd, const char *name, const char *source_path)
int dd_copy_file_unpack(struct dump_dir *dd, const char *name, const char *source_path)
{
- if (!str_is_correct_filename(name))
+ if (!dd_validate_element_name(name))
error_msg_and_die("Cannot test existence. '%s' is not a valid file name", name);
log_debug("unpacking '%s' to '%s' at '%s'", source_path, name, dd->dd_dirname);
diff --git a/tests/dump_dir.at b/tests/dump_dir.at
index 31c320e..4bf479e 100644
--- a/tests/dump_dir.at
+++ b/tests/dump_dir.at
@@ -158,6 +158,177 @@ int main(int argc, char **argv)
}
]])
+## --------------------- ##
+## dd_create_open_delete ##
+## --------------------- ##
+
+AT_TESTFUN([dd_create_open_delete],
+[[
+#include "internal_libreport.h"
+#include <errno.h>
+#include <assert.h>
+
+int main(int argc, char **argv)
+{
+ g_verbose = 3;
+
+ char template[] = "/tmp/XXXXXX/dump_dir";
+
+ char *last_slash = strrchr(template, '/');
+ *last_slash = '\0';
+
+ if (mkdtemp(template) == NULL) {
+ perror("mkdtemp()");
+ return EXIT_FAILURE;
+ }
+
+ *last_slash = '/';
+
+ struct dump_dir *dd = dd_create(template, (uid_t)-1, 0640);
+ assert(strcmp(dd->dd_dirname, template) == 0);
+ assert(dd->dd_fd >= 0);
+ assert(dd->dd_md_fd >= 0);
+
+ struct stat dd_st;
+ assert(fstat(dd->dd_fd, &dd_st) == 0);
+
+ struct stat md_st;
+ assert(fstat(dd->dd_md_fd, &md_st) == 0);
+
+ assert(dd_st.st_uid == md_st.st_uid);
+ assert(dd_st.st_gid == md_st.st_gid);
+ assert((dd_st.st_mode & 0666) == (md_st.st_mode & 0666));
+
+ struct stat path_md_st;
+ assert(fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0);
+ assert(md_st.st_ino = path_md_st.st_ino);
+
+ dd_create_basic_files(dd, (uid_t)-1, NULL);
+ dd_save_text(dd, FILENAME_TYPE, "attest");
+
+ dd_close(dd);
+ dd = NULL;
+
+ dd = dd_opendir(template, 0);
+ assert(dd != NULL);
+ assert(strcmp(dd->dd_dirname, template) == 0);
+ assert(dd->dd_fd >= 0);
+ assert(dd->dd_md_fd < 0);
+
+ dd_delete(dd);
+
+ assert(stat(template, &dd_st) != 0);
+
+ *last_slash = '\0';
+ assert(rmdir(template) == 0);
+ return EXIT_SUCCESS;
+}
+]])
+
+## -------------------------- ##
+## dd_sanitize_mode_and_owner ##
+## -------------------------- ##
+
+AT_TESTFUN([dd_sanitize_mode_and_owner],
+[[
+#include "internal_libreport.h"
+#include <errno.h>
+#include <assert.h>
+
+int main(int argc, char **argv)
+{
+ g_verbose = 3;
+
+ char template[] = "/tmp/XXXXXX/dump_dir";
+
+ char *last_slash = strrchr(template, '/');
+ *last_slash = '\0';
+
+ if (mkdtemp(template) == NULL) {
+ perror("mkdtemp()");
+ return EXIT_FAILURE;
+ }
+
+ *last_slash = '/';
+
+ /* Prepare a directory for chmod test, use mode 0600 and chmod it to 0640 */
+ struct dump_dir *dd = dd_create(template, (uid_t)-1, 0600);
+ assert(dd != NULL);
+
+ {
+ struct stat path_md_st;
+ assert((fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0) || !"Failed initialize meta-data");
+ assert((path_md_st.st_mode & 0077) == 0);
+ }
+
+ dd_create_basic_files(dd, (uid_t)-1, NULL);
+ dd_save_text(dd, FILENAME_TYPE, "attest");
+
+ dd_close(dd);
+
+ /* initialize meta-data */
+ dd = dd_opendir(template, DD_OPEN_FD_ONLY);
+ /* reopen for writing */
+ dd = dd_fdopendir(dd, /*for writing*/0);
+ assert(dd != NULL);
+
+ assert(fchmod(dd->dd_fd, 0750) == 0);
+ dd->mode = 0640;
+
+ fprintf(stderr, "Going to sanitize\n");
+ dd_sanitize_mode_and_owner(dd);
+ fprintf(stderr, "Sanitized\n");
+
+ {
+ DIR *d = opendir(template);
+ struct dirent *dent;
+ while ((dent = readdir(d)) != NULL)
+ {
+ if ( strcmp(".", dent->d_name) == 0
+ || strcmp("..", dent->d_name) == 0
+ || strcmp(".lock", dent->d_name) == 0
+ || strcmp(".libreport", dent->d_name) == 0
+ )
+ continue;
+
+ struct stat sb;
+ printf("Testing element: %s\n", dent->d_name);
+ assert(fstatat(dd->dd_fd, dent->d_name, &sb, 0) == 0 || !"Cannot stat a regular element");
+ assert((sb.st_mode & 0777) == 0640 || !"Failed to chmod a regular element");
+ }
+ closedir(d);
+ }
+
+ {
+ struct stat path_md_st;
+ assert(fstatat(dd->dd_fd, ".libreport", &path_md_st, 0) == 0 || !"Cannot stat meta-data directory");
+ assert((path_md_st.st_mode & 0777) == 0750 || !"Failed chmod meta-data");
+ }
+
+ int md_dir_fd = openat(dd->dd_fd, ".libreport", O_DIRECTORY);
+ assert(md_dir_fd >= 0 || !"Cannot open meta-data directory");
+ DIR *d = fdopendir(md_dir_fd);
+ struct dirent *dent;
+ while ((dent = readdir(d)) != NULL)
+ {
+ if (strcmp(".", dent->d_name) == 0 || strcmp("..", dent->d_name) == 0)
+ continue;
+
+ struct stat sb;
+ printf("Testing meta-data: %s\n", dent->d_name);
+ assert(fstatat(md_dir_fd, dent->d_name, &sb, 0) == 0 || !"Cannot stat meta-data file");
+ assert((sb.st_mode & 0777) == 0640 || !"Failed to chmod a meta-data file");
+ }
+
+ closedir(d);
+ dd_delete(dd);
+
+ *last_slash = '\0';
+ assert(rmdir(template) == 0);
+ return EXIT_SUCCESS;
+}
+]])
+
## -------------- ##
## recursive_lock ##
## -------------- ##
--
2.1.0