summaryrefslogtreecommitdiffstats
path: root/libreport/src/lib/dump_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'libreport/src/lib/dump_dir.c')
-rw-r--r--libreport/src/lib/dump_dir.c839
1 files changed, 0 insertions, 839 deletions
diff --git a/libreport/src/lib/dump_dir.c b/libreport/src/lib/dump_dir.c
deleted file mode 100644
index 8891f911..00000000
--- a/libreport/src/lib/dump_dir.c
+++ /dev/null
@@ -1,839 +0,0 @@
-/*
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <sys/utsname.h>
-#include "libreport.h"
-#include "strbuf.h"
-
-// Locking logic:
-//
-// The directory is locked by creating a symlink named .lock inside it,
-// whose value (where it "points to") is the pid of locking process.
-// We use symlink, not an ordinary file, because symlink creation
-// is an atomic operation.
-//
-// There are two cases where after .lock creation, we might discover
-// that directory is not really free:
-// * another process just created new directory, but didn't manage
-// to lock it before us.
-// * another process is deleting the directory, and we managed to sneak in
-// and create .lock after it deleted all files (including .lock)
-// but before it rmdir'ed the empty directory.
-//
-// Both these cases are detected by the fact that file named "time"
-// is not present (it must be present in any valid dump dir).
-// If after locking the dir we don't see time file, we remove the lock
-// at once and back off. What happens in concurrent processes
-// we interfered with?
-// * "create new dump dir" process just re-tries locking.
-// * "delete dump dir" process just retries rmdir.
-//
-// There is another case when we don't find time file:
-// when the directory is not really a *dump* dir - user gave us
-// an ordinary directory name by mistake.
-// We detect it by bailing out of "lock, check time file; sleep
-// and retry if it doesn't exist" loop using a counter.
-//
-// To make locking work reliably, it's important to set timeouts
-// correctly. For example, dd_create should retry locking
-// its newly-created directory much faster than dd_opendir
-// tries to lock the directory it tries to open.
-
-
-// How long to sleep between "symlink fails with EEXIST,
-// readlink fails with ENOENT" tries. Someone just unlocked the dir.
-// We never bail out in this case, we retry forever.
-// The value can be really small:
-#define SYMLINK_RETRY_USLEEP (10*1000)
-
-// How long to sleep when lock file with valid pid is seen by dd_opendir
-// (we are waiting for other process to unlock or die):
-#define WAIT_FOR_OTHER_PROCESS_USLEEP (500*1000)
-
-// How long to sleep when lock file with valid pid is seen by dd_create
-// (some idiot jumped the gun and locked the dir we just created).
-// Must not be the same as WAIT_FOR_OTHER_PROCESS_USLEEP (we depend on this)
-// and should be small (we have the priority in locking, this is OUR dir):
-#define CREATE_LOCK_USLEEP (10*1000)
-
-// How long to sleep after we locked a dir, found no time file
-// (either we are racing with someone, or it's not a dump dir)
-// and unlocked it;
-// and after how many tries to give up and declare it's not a dump dir:
-#define NO_TIME_FILE_USLEEP (50*1000)
-#define NO_TIME_FILE_COUNT 10
-
-// How long to sleep after we unlocked an empty dir, but then rmdir failed
-// (some idiot jumped the gun and locked the dir we are deleting);
-// and after how many tries to give up:
-#define RMDIR_FAIL_USLEEP (10*1000)
-#define RMDIR_FAIL_COUNT 50
-
-
-static char *load_text_file(const char *path, unsigned flags);
-
-static bool isdigit_str(const char *str)
-{
- do
- {
- if (*str < '0' || *str > '9') return false;
- str++;
- } while (*str);
- return true;
-}
-
-static bool exist_file_dir(const char *path)
-{
- struct stat buf;
- if (stat(path, &buf) == 0)
- {
- if (S_ISDIR(buf.st_mode) || S_ISREG(buf.st_mode))
- {
- return true;
- }
- }
- return false;
-}
-
-/* Return values:
- * -1: error (in this case, errno is 0 if error message is already logged)
- * 0: failed to lock (someone else has it locked)
- * 1: success
- */
-static int get_and_set_lock(const char* lock_file, const char* pid)
-{
- while (symlink(pid, lock_file) != 0)
- {
- if (errno != EEXIST)
- {
- if (errno != ENOENT && errno != ENOTDIR && errno != EACCES)
- {
- perror_msg("Can't create lock file '%s'", lock_file);
- errno = 0;
- }
- return -1;
- }
-
- char pid_buf[sizeof(pid_t)*3 + 4];
- ssize_t r = readlink(lock_file, pid_buf, sizeof(pid_buf) - 1);
- if (r < 0)
- {
- if (errno == ENOENT)
- {
- /* Looks like lock_file was deleted */
- usleep(SYMLINK_RETRY_USLEEP); /* avoid CPU eating loop */
- continue;
- }
- perror_msg("Can't read lock file '%s'", lock_file);
- errno = 0;
- return -1;
- }
- pid_buf[r] = '\0';
-
- if (strcmp(pid_buf, pid) == 0)
- {
- log("Lock file '%s' is already locked by us", lock_file);
- return 0;
- }
- if (isdigit_str(pid_buf))
- {
- char pid_str[sizeof("/proc/") + sizeof(pid_buf)];
- sprintf(pid_str, "/proc/%s", pid_buf);
- if (access(pid_str, F_OK) == 0)
- {
- log("Lock file '%s' is locked by process %s", lock_file, pid_buf);
- return 0;
- }
- log("Lock file '%s' was locked by process %s, but it crashed?", lock_file, pid_buf);
- }
- /* The file may be deleted by now by other process. Ignore ENOENT */
- if (unlink(lock_file) != 0 && errno != ENOENT)
- {
- perror_msg("Can't remove stale lock file '%s'", lock_file);
- errno = 0;
- return -1;
- }
- }
-
- VERB1 log("Locked '%s'", lock_file);
- return 1;
-}
-
-static int dd_lock(struct dump_dir *dd, unsigned sleep_usec, int flags)
-{
- if (dd->locked)
- error_msg_and_die("Locking bug on '%s'", dd->dd_dirname);
-
- char pid_buf[sizeof(long)*3 + 2];
- sprintf(pid_buf, "%lu", (long)getpid());
-
- unsigned dirname_len = strlen(dd->dd_dirname);
- char lock_buf[dirname_len + sizeof("/.lock")];
- strcpy(lock_buf, dd->dd_dirname);
- strcpy(lock_buf + dirname_len, "/.lock");
-
- unsigned count = NO_TIME_FILE_COUNT;
- retry:
- while (1)
- {
- int r = get_and_set_lock(lock_buf, pid_buf);
- if (r < 0)
- return r; /* error */
- if (r > 0)
- break; /* locked successfully */
- /* Other process has the lock, wait for it to go away */
- usleep(sleep_usec);
- }
-
- /* Are we called by dd_opendir (as opposed to dd_create)? */
- if (sleep_usec == WAIT_FOR_OTHER_PROCESS_USLEEP) /* yes */
- {
- strcpy(lock_buf + dirname_len, "/time");
- if (access(lock_buf, F_OK) != 0)
- {
- /* time file doesn't exist. We managed to lock the directory
- * which was just created by somebody else, or is almost deleted
- * by delete_file_dir.
- * Unlock and back off.
- */
- strcpy(lock_buf + dirname_len, "/.lock");
- xunlink(lock_buf);
- VERB1 log("Unlocked '%s' (no time file)", lock_buf);
- if (--count == 0)
- {
- errno = EISDIR; /* "this is an ordinary dir, not dump dir" */
- return -1;
- }
- usleep(NO_TIME_FILE_USLEEP);
- goto retry;
- }
- }
-
- dd->locked = true;
- return 0;
-}
-
-static void dd_unlock(struct dump_dir *dd)
-{
- if (dd->locked)
- {
- dd->locked = 0;
-
- unsigned dirname_len = strlen(dd->dd_dirname);
- char lock_buf[dirname_len + sizeof("/.lock")];
- strcpy(lock_buf, dd->dd_dirname);
- strcpy(lock_buf + dirname_len, "/.lock");
- xunlink(lock_buf);
-
- VERB1 log("Unlocked '%s'", lock_buf);
- }
-}
-
-static inline struct dump_dir *dd_init(void)
-{
- return (struct dump_dir*)xzalloc(sizeof(struct dump_dir));
-}
-
-int dd_exist(struct dump_dir *dd, const char *path)
-{
- char *full_path = concat_path_file(dd->dd_dirname, path);
- int ret = exist_file_dir(full_path);
- free(full_path);
- return ret;
-}
-
-void dd_close(struct dump_dir *dd)
-{
- if (!dd)
- return;
-
- dd_unlock(dd);
- if (dd->next_dir)
- {
- closedir(dd->next_dir);
- /* free(dd->next_dir); - WRONG! */
- }
-
- free(dd->dd_dirname);
- free(dd);
-}
-
-static char* rm_trailing_slashes(const char *dir)
-{
- unsigned len = strlen(dir);
- while (len != 0 && dir[len-1] == '/')
- len--;
- return xstrndup(dir, len);
-}
-
-struct dump_dir *dd_opendir(const char *dir, int flags)
-{
- struct dump_dir *dd = dd_init();
-
- dir = dd->dd_dirname = rm_trailing_slashes(dir);
-
- struct stat stat_buf;
- stat(dir, &stat_buf);
- /* & 0666 should remove the executable bit */
- dd->mode = (stat_buf.st_mode & 0666);
-
- 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 */
- if (stat(dir, &stat_buf) == 0
- && S_ISDIR(stat_buf.st_mode)
- && access(dir, R_OK) == 0
- ) {
- return dd;
- }
- }
- if (errno == EISDIR)
- {
- /* EISDIR: dd_lock can lock the dir, but it sees no time file there,
- * even after it retried many times. It must be an ordinary directory!
- *
- * Without this check, e.g. abrt-action-print happily prints any current
- * directory when run without arguments, because its option -d DIR
- * defaults to "."!
- */
- error_msg("'%s' is not a dump directory", dir);
- }
- else if (errno == ENOENT || errno == ENOTDIR)
- {
- if (!(flags & DD_FAIL_QUIETLY_ENOENT))
- error_msg("'%s' does not exist", dir);
- }
- else
- {
- if (!(flags & DD_FAIL_QUIETLY_EACCES))
- perror_msg("Can't access '%s'", dir);
- }
- dd_close(dd);
- return NULL;
- }
-
- dd->dd_uid = (uid_t)-1L;
- dd->dd_gid = (gid_t)-1L;
- if (geteuid() == 0)
- {
- /* In case caller would want to create more files, he'll need uid:gid */
- struct stat stat_buf;
- if (stat(dir, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode))
- {
- error_msg("Can't stat '%s', or it is not a directory", dir);
- dd_close(dd);
- return NULL;
- }
- dd->dd_uid = stat_buf.st_uid;
- dd->dd_gid = stat_buf.st_gid;
- }
-
- return dd;
-}
-
-/* Create a fresh empty debug dump dir.
- *
- * Security: we should not allow users to write new files or write
- * into existing ones, but they should be able to read them.
- *
- * @param uid
- * Crashed application's User Id
- *
- * We currently have only three callers:
- * kernel oops hook: uid -> not saved, so everyone can steal and work with it
- * this hook runs under 0:0
- * ccpp hook: uid=uid of crashed user's binary
- * this hook runs under 0:0
- * python hook: uid=uid of crashed user's script
- * this hook runs under abrt:gid
- *
- * Currently, we set dir's gid to passwd(uid)->pw_gid parameter, and we set uid to
- * abrt's user id. We do not allow write access to group.
- */
-struct dump_dir *dd_create(const char *dir, uid_t uid, mode_t mode)
-{
- /* a little trick to copy read bits from file mode to exec bit of dir mode*/
- mode_t dir_mode = mode | ((mode & 0444) >> 2);
- struct dump_dir *dd = dd_init();
-
- dd->mode = mode;
-
- /* Unlike dd_opendir, can't use realpath: the directory doesn't exist yet,
- * realpath will always return NULL. We don't really have to:
- * dd_opendir(".") makes sense, dd_create(".") does not.
- */
- dir = dd->dd_dirname = rm_trailing_slashes(dir);
-
- const char *last_component = strrchr(dir, '/');
- if (last_component)
- last_component++;
- else
- last_component = dir;
- if (dot_or_dotdot(last_component))
- {
- /* dd_create("."), dd_create(".."), dd_create("dir/."),
- * dd_create("dir/..") and similar are madness, refuse them.
- */
- error_msg("Bad dir name '%s'", dir);
- dd_close(dd);
- return NULL;
- }
-
- bool created_parents = false;
- try_again:
- /* Was creating it with mode 0700 and user as the owner, but this allows
- * the user to replace any file in the directory, changing security-sensitive data
- * (e.g. "uid", "analyzer", "executable")
- */
- if (mkdir(dir, dir_mode) == -1)
- {
- int err = errno;
- if (!created_parents && errno == ENOENT)
- {
- char *p = dd->dd_dirname + 1;
- while ((p = strchr(p, '/')) != NULL)
- {
- *p = '\0';
- int r = (mkdir(dd->dd_dirname, 0755) == 0 || errno == EEXIST);
- *p++ = '/';
- if (!r)
- goto report_err;
- }
- created_parents = true;
- goto try_again;
- }
- report_err:
- errno = err;
- perror_msg("Can't create directory '%s'", dir);
- dd_close(dd);
- return NULL;
- }
-
- if (dd_lock(dd, CREATE_LOCK_USLEEP, /*flags:*/ 0) < 0)
- {
- dd_close(dd);
- return NULL;
- }
-
- /* mkdir's mode (above) can be affected by umask, fix it */
- if (chmod(dir, dir_mode) == -1)
- {
- perror_msg("can't change mode of '%s'", dir);
- dd_close(dd);
- return NULL;
- }
-
- dd->dd_uid = (uid_t)-1L;
- dd->dd_gid = (gid_t)-1L;
- if (uid != (uid_t)-1L)
- {
- /* Get ABRT's user id */
- dd->dd_uid = 0;
- struct passwd *pw = getpwnam("abrt");
- if (pw)
- dd->dd_uid = pw->pw_uid;
- else
- error_msg("user 'abrt' does not exist, using uid 0");
-
- /* Get crashed application's group id */
- /*dd->dd_gid = 0; - dd_init did this already */
- pw = getpwuid(uid);
- if (pw)
- dd->dd_gid = pw->pw_gid;
- else
- error_msg("User %lu does not exist, using gid 0", (long)uid);
-
- if (chown(dir, dd->dd_uid, dd->dd_gid) == -1)
- {
- perror_msg("can't change '%s' ownership to %lu:%lu", dir,
- (long)dd->dd_uid, (long)dd->dd_gid);
- }
- }
-
- return dd;
-}
-
-void dd_create_basic_files(struct dump_dir *dd, uid_t uid)
-{
- char long_str[sizeof(long) * 3 + 2];
-
- time_t t = time(NULL);
- sprintf(long_str, "%lu", (long)t);
- dd_save_text(dd, FILENAME_TIME, long_str);
-
- /* it doesn't make sense to create the uid file if uid == -1 */
- if (uid != (uid_t)-1L)
- {
- sprintf(long_str, "%li", (long)uid);
- dd_save_text(dd, FILENAME_UID, long_str);
- }
-
- struct utsname buf;
- uname(&buf); /* never fails */
- dd_save_text(dd, FILENAME_KERNEL, buf.release);
- dd_save_text(dd, FILENAME_ARCHITECTURE, buf.machine);
- dd_save_text(dd, FILENAME_HOSTNAME, buf.nodename);
-
- char *release = load_text_file("/etc/system-release",
- DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
- if (!release)
- release = load_text_file("/etc/redhat-release", /*flags:*/ 0);
- dd_save_text(dd, FILENAME_OS_RELEASE, release);
- free(release);
-}
-
-void dd_sanitize_mode_and_owner(struct dump_dir *dd)
-{
- /* Don't sanitize if we aren't run under root:
- * we assume that during file creation (by whatever means,
- * even by "hostname >file" in abrt_event.conf)
- * normal umask-based mode setting takes care of correct mode,
- * and uid:gid is, of course, set to user's uid and gid.
- *
- * For root operating on /var/spool/abrt/USERS_PROBLEM, this isn't true:
- * "hostname >file", for example, would create file OWNED BY ROOT!
- * This routine resets mode and uid:gid for all such files.
- */
- if (dd->dd_uid == (uid_t)-1)
- return;
-
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
-
- DIR *d = opendir(dd->dd_dirname);
- if (!d)
- return;
-
- struct dirent *dent;
- while ((dent = readdir(d)) != NULL)
- {
- if (dent->d_name[0] == '.') /* ".lock", ".", ".."? skip */
- continue;
- char *full_path = concat_path_file(dd->dd_dirname, dent->d_name);
- struct stat statbuf;
- if (lstat(full_path, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
- {
- if ((statbuf.st_mode & 0777) != dd->mode)
- chmod(full_path, dd->mode);
- if (statbuf.st_uid != dd->dd_uid || statbuf.st_gid != dd->dd_gid)
- {
- if (chown(full_path, dd->dd_uid, dd->dd_gid) != 0)
- {
- perror_msg("can't change '%s' ownership to %lu:%lu", full_path,
- (long)dd->dd_uid, (long)dd->dd_gid);
- }
- }
- }
- free(full_path);
- }
- closedir(d);
-}
-
-static int delete_file_dir(const char *dir, bool skip_lock_file)
-{
- DIR *d = opendir(dir);
- if (!d)
- {
- /* 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)
- {
- errno = 0;
- return 0;
- }
- return -1;
- }
-
- bool unlink_lock_file = false;
- struct dirent *dent;
- while ((dent = readdir(d)) != NULL)
- {
- if (dot_or_dotdot(dent->d_name))
- continue;
- if (skip_lock_file && strcmp(dent->d_name, ".lock") == 0)
- {
- unlink_lock_file = true;
- continue;
- }
- char *full_path = concat_path_file(dir, dent->d_name);
- if (unlink(full_path) == -1 && errno != ENOENT)
- {
- int err = 0;
- if (errno == EISDIR)
- {
- errno = 0;
- err = delete_file_dir(full_path, /*skip_lock_file:*/ false);
- }
- if (errno || err)
- {
- perror_msg("Can't remove '%s'", full_path);
- free(full_path);
- closedir(d);
- return -1;
- }
- }
- free(full_path);
- }
- closedir(d);
-
- /* Here we know for sure that all files/subdirs we found via readdir
- * were deleted successfully. If rmdir below fails, we assume someone
- * is racing with us and created a new file.
- */
-
- if (unlink_lock_file)
- {
- char *full_path = concat_path_file(dir, ".lock");
- xunlink(full_path);
- free(full_path);
-
- unsigned cnt = RMDIR_FAIL_COUNT;
- do {
- if (rmdir(dir) == 0)
- return 0;
- /* Someone locked the dir after unlink, but before rmdir.
- * This "someone" must be dd_lock().
- * It detects this (by seeing that there is no time file)
- * and backs off at once. So we need to just retry rmdir,
- * with minimal sleep.
- */
- usleep(RMDIR_FAIL_USLEEP);
- } while (--cnt != 0);
- }
-
- int r = rmdir(dir);
- if (r)
- perror_msg("Can't remove directory '%s'", dir);
- return r;
-}
-
-int dd_delete(struct dump_dir *dd)
-{
- int r = delete_file_dir(dd->dd_dirname, /*skip_lock_file:*/ true);
- dd->locked = 0; /* delete_file_dir already removed .lock */
- dd_close(dd);
- return r;
-}
-
-static char *load_text_file(const char *path, unsigned flags)
-{
- FILE *fp = fopen(path, "r");
- if (!fp)
- {
- if (!(flags & DD_FAIL_QUIETLY_ENOENT))
- perror_msg("Can't open file '%s'", path);
- return (flags & DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE ? NULL : xstrdup(""));
- }
-
- struct strbuf *buf_content = strbuf_new();
- int oneline = 0;
- int ch;
- while ((ch = fgetc(fp)) != EOF)
- {
-//TODO? \r -> \n?
-//TODO? strip trailing spaces/tabs?
- if (ch == '\n')
- oneline = (oneline << 1) | 1;
- if (ch == '\0')
- ch = ' ';
- if (isspace(ch) || ch >= ' ') /* used !iscntrl, but it failed on unicode */
- strbuf_append_char(buf_content, ch);
- }
- fclose(fp);
-
- char last = oneline != 0 ? buf_content->buf[buf_content->len - 1] : 0;
- if (last == '\n')
- {
- /* If file contains exactly one '\n' and it is at the end, remove it.
- * This enables users to use simple "echo blah >file" in order to create
- * short string items in dump dirs.
- */
- if (oneline == 1)
- buf_content->buf[--buf_content->len] = '\0';
- }
- else /* last != '\n' */
- {
- /* Last line is unterminated, fix it */
- /* Cases: */
- /* oneline=0: "qwe" - DONT fix this! */
- /* oneline=1: "qwe\nrty" - two lines in fact */
- /* oneline>1: "qwe\nrty\uio" */
- if (oneline >= 1)
- strbuf_append_char(buf_content, '\n');
- }
-
- return strbuf_free_nobuf(buf_content);
-}
-
-static bool save_binary_file(const char *path, const char* data, unsigned size, uid_t uid, gid_t gid, mode_t mode)
-{
- /* the mode is set by the caller, see dd_create() for security analysis */
- unlink(path);
- int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
- if (fd < 0)
- {
- perror_msg("Can't open file '%s'", path);
- return false;
- }
-
- if (uid != (uid_t)-1L)
- {
- if (fchown(fd, uid, gid) == -1)
- {
- perror_msg("can't change '%s' ownership to %lu:%lu", path, (long)uid, (long)gid);
- }
- }
-
- unsigned r = full_write(fd, data, size);
- close(fd);
- if (r != size)
- {
- error_msg("Can't save file '%s'", path);
- return false;
- }
-
- return true;
-}
-
-char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags)
-{
-// if (!dd->locked)
-// error_msg_and_die("dump_dir is not opened"); /* bug */
-
- /* Compat with old abrt dumps. Remove in abrt-2.1 */
- if (strcmp(name, "release") == 0)
- name = FILENAME_OS_RELEASE;
-
- char *full_path = concat_path_file(dd->dd_dirname, name);
- char *ret = load_text_file(full_path, flags);
- free(full_path);
-
- return ret;
-}
-
-char* dd_load_text(const struct dump_dir *dd, const char *name)
-{
- return dd_load_text_ext(dd, name, /*flags:*/ 0);
-}
-
-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 */
-
- char *full_path = concat_path_file(dd->dd_dirname, name);
- save_binary_file(full_path, data, strlen(data), dd->dd_uid, dd->dd_gid, dd->mode);
- free(full_path);
-}
-
-void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, unsigned size)
-{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
-
- char *full_path = concat_path_file(dd->dd_dirname, name);
- save_binary_file(full_path, data, size, dd->dd_uid, dd->dd_gid, dd->mode);
- free(full_path);
-}
-
-void add_reported_to(struct dump_dir *dd, const char *line)
-{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
-
- char *reported_to = dd_load_text_ext(dd, FILENAME_REPORTED_TO, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
- if (reported_to)
- {
- unsigned len_line = strlen(line);
- char *p = reported_to;
- while (*p)
- {
- if (strncmp(p, line, len_line) == 0 && (p[len_line] == '\n' || p[len_line] == '\0'))
- goto ret;
- p = strchrnul(p, '\n');
- if (!*p)
- break;
- p++;
- }
- if (p != reported_to && p[-1] != '\n')
- reported_to = append_to_malloced_string(reported_to, "\n");
- reported_to = append_to_malloced_string(reported_to, line);
- reported_to = append_to_malloced_string(reported_to, "\n");
- }
- else
- reported_to = xasprintf("%s\n", line);
- dd_save_text(dd, FILENAME_REPORTED_TO, reported_to);
- ret:
- free(reported_to);
-}
-
-DIR *dd_init_next_file(struct dump_dir *dd)
-{
-// if (!dd->locked)
-// error_msg_and_die("dump_dir is not opened"); /* bug */
-
- if (dd->next_dir)
- closedir(dd->next_dir);
-
- dd->next_dir = opendir(dd->dd_dirname);
- if (!dd->next_dir)
- {
- error_msg("Can't open directory '%s'", dd->dd_dirname);
- }
-
- return dd->next_dir;
-}
-
-int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name)
-{
- if (dd->next_dir == NULL)
- return 0;
-
- struct dirent *dent;
- while ((dent = readdir(dd->next_dir)) != NULL)
- {
- if (is_regular_file(dent, dd->dd_dirname))
- {
- if (short_name)
- *short_name = xstrdup(dent->d_name);
- if (full_name)
- *full_name = concat_path_file(dd->dd_dirname, dent->d_name);
- return 1;
- }
- }
-
- closedir(dd->next_dir);
- dd->next_dir = NULL;
- return 0;
-}
-
-/* Utility function */
-void delete_dump_dir(const char *dirname)
-{
- struct dump_dir *dd = dd_opendir(dirname, /*flags:*/ 0);
- if (dd)
- {
- dd_delete(dd);
- }
-}