double get_dirsize(const char *pPath) { DIR *dp = opendir(pPath); if (dp == NULL) return 0; struct dirent *ep; struct stat statbuf; double size = 0; while ((ep = readdir(dp)) != NULL) { if (dot_or_dotdot(ep->d_name)) continue; char *dname = concat_path_file(pPath, ep->d_name); if (lstat(dname, &statbuf) != 0) { goto next; } if (S_ISDIR(statbuf.st_mode)) { size += get_dirsize(dname); } else if (S_ISREG(statbuf.st_mode)) { size += statbuf.st_size; } next: free(dname); } closedir(dp); return size; }
static double get_dir_size(const char *dirname, char **worst_file, double *worst_file_size) { DIR *dp = opendir(dirname); if (!dp) return 0; /* "now" is used only if caller wants to know worst_file */ time_t now = worst_file ? time(NULL) : 0; struct dirent *dent; struct stat stats; double size = 0; while ((dent = readdir(dp)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; char *fullname = concat_path_file(dirname, dent->d_name); if (lstat(fullname, &stats) != 0) { free(fullname); continue; } if (S_ISDIR(stats.st_mode)) { double sz = get_dir_size(fullname, worst_file, worst_file_size); size += sz; } else if (S_ISREG(stats.st_mode)) { double sz = stats.st_size; size += sz; if (worst_file) { /* Calculate "weighted" size and age * w = sz_kbytes * age_mins */ sz /= 1024; long age = (now - stats.st_mtime) / 60; if (age > 0) sz *= age; if (sz > *worst_file_size) { *worst_file_size = sz; free(*worst_file); *worst_file = fullname; fullname = NULL; } } } free(fullname); } closedir(dp); return size; }
/* The function expects that FILENAME_COUNT dump dir element is created by * abrtd after all post-create events are successfully done. Thus if * FILENAME_COUNT element doesn't exist abrtd can consider the dump directory * as unprocessed. * * Relying on content of dump directory has one problem. If a hook provides * FILENAME_COUNT abrtd will consider the dump directory as processed. */ static void mark_unprocessed_dump_dirs_not_reportable(const char *path) { log_notice("Searching for unprocessed dump directories"); DIR *dp = opendir(path); if (!dp) { perror_msg("Can't open directory '%s'", path); return; } struct dirent *dent; while ((dent = readdir(dp)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; /* skip "." and ".." */ char *full_name = concat_path_file(path, dent->d_name); struct stat stat_buf; if (stat(full_name, &stat_buf) != 0) { perror_msg("Can't access path '%s'", full_name); goto next_dd; } if (S_ISDIR(stat_buf.st_mode) == 0) /* This is expected. The dump location contains some aux files */ goto next_dd; struct dump_dir *dd = dd_opendir(full_name, /*flags*/0); if (dd) { if (!problem_dump_dir_is_complete(dd) && !dd_exist(dd, FILENAME_NOT_REPORTABLE)) { log_warning("Marking '%s' not reportable (no '"FILENAME_COUNT"' item)", full_name); dd_save_text(dd, FILENAME_NOT_REPORTABLE, _("The problem data are " "incomplete. This usually happens when a problem " "is detected while computer is shutting down or " "user is logging out. In order to provide " "valuable problem reports, ABRT will not allow " "you to submit this problem. If you have time and " "want to help the developers in their effort to " "sort out this problem, please contact them directly.")); } dd_close(dd); } next_dd: free(full_name); } closedir(dp); }
static void scan_directory_and_add_to_dirlist(const char *path) { DIR *dp = opendir(path); if (!dp) { /* We don't want to yell if, say, $HOME/.abrt/spool doesn't exist */ //perror_msg("Can't open directory '%s'", path); return; } struct dirent *dent; while ((dent = readdir(dp)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; /* skip "." and ".." */ char *full_name = concat_path_file(path, dent->d_name); struct stat statbuf; if (stat(full_name, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) add_directory_to_dirlist(full_name); free(full_name); } closedir(dp); if (inotify_fd >= 0 && inotify_add_watch(inotify_fd, path, 0 // | IN_ATTRIB // Metadata changed // | IN_CLOSE_WRITE // File opened for writing was closed | IN_CREATE // File/directory created in watched directory | IN_DELETE // File/directory deleted from watched directory | IN_DELETE_SELF // Watched file/directory was itself deleted | IN_MODIFY // File was modified | IN_MOVE_SELF // Watched file/directory was itself moved | IN_MOVED_FROM // File moved out of watched directory | IN_MOVED_TO // File moved into watched directory ) < 0) { perror_msg("inotify_add_watch failed on '%s'", path); } }
/* This function is called once for every file system object that fts encounters. fts performs a depth-first traversal. A directory is usually processed twice, first with fts_info == FTS_D, and later, after all of its entries have been processed, with FTS_DP. Return RM_ERROR upon error, RM_USER_DECLINED for a negative response to an interactive prompt, and otherwise, RM_OK. */ static enum RM_status rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) { switch (ent->fts_info) { case FTS_D: /* preorder directory */ if (! x->recursive && !(x->remove_empty_directories && is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath))) { /* This is the first (pre-order) encounter with a directory that we cannot delete. Not recursive, and it's not an empty directory (if we're removing them) so arrange to skip contents. */ int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR; error (0, err, _("cannot remove %s"), quote (ent->fts_path)); mark_ancestor_dirs (ent); fts_skip_tree (fts, ent); return RM_ERROR; } /* Perform checks that can apply only for command-line arguments. */ if (ent->fts_level == FTS_ROOTLEVEL) { if (strip_trailing_slashes (ent->fts_path)) ent->fts_pathlen = strlen (ent->fts_path); /* If the basename of a command line argument is "." or "..", diagnose it and do nothing more with that argument. */ if (dot_or_dotdot (last_component (ent->fts_accpath))) { error (0, 0, _("cannot remove directory: %s"), quote (ent->fts_path)); fts_skip_tree (fts, ent); return RM_ERROR; } /* If a command line argument resolves to "/" (and --preserve-root is in effect -- default) diagnose and skip it. */ if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp)) { ROOT_DEV_INO_WARN (ent->fts_path); fts_skip_tree (fts, ent); return RM_ERROR; } } { Ternary is_empty_directory; enum RM_status s = prompt (fts, ent, true /*is_dir*/, x, PA_DESCEND_INTO_DIR, &is_empty_directory); if (s == RM_OK && is_empty_directory == T_YES) { /* When we know (from prompt when in interactive mode) that this is an empty directory, don't prompt twice. */ s = excise (fts, ent, x, true); fts_skip_tree (fts, ent); } if (s != RM_OK) { mark_ancestor_dirs (ent); fts_skip_tree (fts, ent); } return s; } case FTS_F: /* regular file */ case FTS_NS: /* stat(2) failed */ case FTS_SL: /* symbolic link */ case FTS_SLNONE: /* symbolic link without target */ case FTS_DP: /* postorder directory */ case FTS_DNR: /* unreadable directory */ case FTS_NSOK: /* e.g., dangling symlink */ case FTS_DEFAULT: /* none of the above */ { /* With --one-file-system, do not attempt to remove a mount point. fts' FTS_XDEV ensures that we don't process any entries under the mount point. */ if (ent->fts_info == FTS_DP && x->one_file_system && FTS_ROOTLEVEL < ent->fts_level && ent->fts_statp->st_dev != fts->fts_dev) { mark_ancestor_dirs (ent); error (0, 0, _("skipping %s, since it's on a different device"), quote (ent->fts_path)); return RM_ERROR; } bool is_dir = ent->fts_info == FTS_DP || ent->fts_info == FTS_DNR; enum RM_status s = prompt (fts, ent, is_dir, x, PA_REMOVE_DIR, NULL); if (s != RM_OK) return s; return excise (fts, ent, x, is_dir); } case FTS_DC: /* directory that causes cycles */ emit_cycle_warning (ent->fts_path); fts_skip_tree (fts, ent); return RM_ERROR; case FTS_ERR: /* Various failures, from opendir to ENOMEM, to failure to "return" to preceding directory, can provoke this. */ error (0, ent->fts_errno, _("traversal failed: %s"), quote (ent->fts_path)); fts_skip_tree (fts, ent); return RM_ERROR; default: error (0, 0, _("unexpected failure: fts_info=%d: %s\n" "please report to %s"), ent->fts_info, quote (ent->fts_path), PACKAGE_BUGREPORT); abort (); } }
int copy_file_recursive(const char *source, const char *dest) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; int retval = 0; int dest_exists = 0; if (strcmp(source, ".lock") == 0) goto skip; if (stat(source, &source_stat) < 0) { perror_msg("Can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { perror_msg("Can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { error_msg("Target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode = source_stat.st_mode; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { perror_msg("Can't create directory '%s'", dest); return -1; } } /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto ret; } while (retval == 0 && (d = readdir(dp)) != NULL) { char *new_source, *new_dest; if (dot_or_dotdot(d->d_name)) continue; new_source = concat_path_file(source, d->d_name); new_dest = concat_path_file(dest, d->d_name); if (copy_file_recursive(new_source, new_dest) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); goto ret; } if (S_ISREG(source_stat.st_mode)) { int src_fd; int dst_fd; mode_t new_mode; src_fd = open(source, O_RDONLY); if (src_fd < 0) { perror_msg("Can't open '%s'", source); return -1; } /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; // security problem versus (sym)link attacks // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) retval = -1; close(src_fd); /* Careful: do check that buffered writes succeeded... */ if (close(dst_fd) < 0) { perror_msg("Error writing to '%s'", dest); retval = -1; } else { /* (Try to) copy atime and mtime */ struct timeval atime_mtime[2]; atime_mtime[0].tv_sec = source_stat.st_atime; // note: if "st_atim.tv_nsec" doesn't compile, try "st_atimensec": atime_mtime[0].tv_usec = source_stat.st_atim.tv_nsec / 1000; atime_mtime[1].tv_sec = source_stat.st_mtime; atime_mtime[1].tv_usec = source_stat.st_mtim.tv_nsec / 1000; // note: can use utimensat when it is more widely supported: utimes(dest, atime_mtime); } goto ret; } /* Neither dir not regular file: skip */ skip: log_warning("Skipping '%s'", source); ret: return retval; }
/* This function is run after each post-create event is finished (there may be * multiple such events). * * It first checks if there is CORE_BACKTRACE or UUID item in the dump dir * we are processing. * * If there is a CORE_BACKTRACE, it iterates over all other dump * directories and computes similarity to their core backtraces (if any). * If one of them is similar enough to be considered duplicate, the function * saves the path to the dump directory in question and returns 1 to indicate * that we have indeed found a duplicate of currently processed dump directory. * No more events are processed and program prints the path to the other * directory and returns failure. * * If there is an UUID item (and no core backtrace), the function again * iterates over all other dump directories and compares this UUID to their * UUID. If there is a match, the path to the duplicate is saved and 1 is returned. * * If duplicate is not found as described above, the function returns 0 and we * either process remaining events if there are any, or successfully terminate * processing of the current dump directory. */ static int is_crash_a_dup(const char *dump_dir_name, void *param) { int retval = 0; /* defaults to no dup found, "run_event, please continue iterating" */ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); if (!dd) return 0; /* wtf? (error, but will be handled elsewhere later) */ free(type); type = dd_load_text(dd, FILENAME_TYPE); free(executable); executable = dd_load_text_ext(dd, FILENAME_EXECUTABLE, DD_FAIL_QUIETLY_ENOENT); char *container_id = dd_load_text_ext(dd, FILENAME_CONTAINER_ID, DD_FAIL_QUIETLY_ENOENT); dup_uuid_init(dd); dup_corebt_init(dd); dd_close(dd); /* dump_dir_name can be relative */ dump_dir_name = realpath(dump_dir_name, NULL); DIR *dir = opendir(g_settings_dump_location); if (dir == NULL) goto end; /* Scan crash dumps looking for a dup */ //TODO: explain why this is safe wrt concurrent runs struct dirent *dent; while ((dent = readdir(dir)) != NULL && crash_dump_dup_name == NULL) { if (dot_or_dotdot(dent->d_name)) continue; /* skip "." and ".." */ const char *ext = strrchr(dent->d_name, '.'); if (ext && strcmp(ext, ".new") == 0) continue; /* skip anything named "<dirname>.new" */ dd = NULL; char *tmp_concat_path = concat_path_file(g_settings_dump_location, dent->d_name); char *dump_dir_name2 = realpath(tmp_concat_path, NULL); if (g_verbose > 1 && !dump_dir_name2) perror_msg("realpath(%s)", tmp_concat_path); free(tmp_concat_path); if (!dump_dir_name2) continue; char *dd_uid = NULL, *dd_type = NULL; char *dd_executable = NULL, *dd_container_id = NULL; if (strcmp(dump_dir_name, dump_dir_name2) == 0) goto next; /* we are never a dup of ourself */ int sv_logmode = logmode; /* Silently ignore any error in the silent log level. */ logmode = g_verbose == 0 ? 0 : sv_logmode; dd = dd_opendir(dump_dir_name2, /*flags:*/ DD_FAIL_QUIETLY_ENOENT | DD_OPEN_READONLY); logmode = sv_logmode; if (!dd) goto next; /* problems from different containers are not duplicates */ if (container_id != NULL) { dd_container_id = dd_load_text_ext(dd, FILENAME_CONTAINER_ID, DD_FAIL_QUIETLY_ENOENT); if (dd_container_id != NULL && strcmp(container_id, dd_container_id) != 0) { goto next; } } /* crashes of different users are not considered duplicates */ dd_uid = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT); if (strcmp(uid, dd_uid)) { goto next; } /* different crash types are not duplicates */ dd_type = dd_load_text_ext(dd, FILENAME_TYPE, DD_FAIL_QUIETLY_ENOENT); if (strcmp(type, dd_type)) { goto next; } /* different executables are not duplicates */ dd_executable = dd_load_text_ext(dd, FILENAME_EXECUTABLE, DD_FAIL_QUIETLY_ENOENT); if ( (executable != NULL && dd_executable == NULL) || (executable == NULL && dd_executable != NULL) || ((executable != NULL && dd_executable != NULL) && strcmp(executable, dd_executable) != 0)) { goto next; } if (dup_uuid_compare(dd) || dup_corebt_compare(dd) ) { crash_dump_dup_name = dump_dir_name2; dump_dir_name2 = NULL; retval = 1; /* "run_event, please stop iterating" */ /* sonce crash_dump_dup_name != NULL now, we exit the loop */ } next: free(dump_dir_name2); dd_close(dd); free(dd_uid); free(dd_type); free(dd_container_id); } closedir(dir); end: free((char*)dump_dir_name); free(container_id); return retval; }
static double get_dir_size(const char *dirname, GList **pp_worst_file_list, GList *preserve_files_list ) { DIR *dp = opendir(dirname); if (!dp) return 0; /* "now" is used only if caller wants to know worst_file */ time_t now = pp_worst_file_list ? time(NULL) : 0; struct dirent *dent; double size = 0; while ((dent = readdir(dp)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; char *fullname = concat_path_file(dirname, dent->d_name); struct stat stats; if (lstat(fullname, &stats) != 0) goto next; if (S_ISDIR(stats.st_mode)) { double sz = get_dir_size(fullname, pp_worst_file_list, preserve_files_list); size += sz; } else if (S_ISREG(stats.st_mode) || S_ISLNK(stats.st_mode)) { double sz = stats.st_size; /* Account for filename and inode storage (approximately). * This also makes even zero-length files to have nonzero cost. */ sz += strlen(dent->d_name) + sizeof(stats); size += sz; if (pp_worst_file_list) { GList *cur = preserve_files_list; while (cur) { //log_warning("'%s' ? '%s'", fullname, *pp); if (strcmp(fullname, (char*)cur->data) == 0) goto next; cur = cur->next; } /* Calculate "weighted" size and age * w = sz_kbytes * age_mins */ sz /= 1024; long age = (now - stats.st_mtime) / 60; if (age > 1) sz *= age; *pp_worst_file_list = insert_name_and_sizes(*pp_worst_file_list, fullname, sz, stats.st_size); } } next: free(fullname); } closedir(dp); return size; }
double get_dirsize_find_largest_dir( const char *pPath, char **worst_dir, const char *excluded) { if (worst_dir) *worst_dir = NULL; DIR *dp = opendir(pPath); if (dp == NULL) return 0; time_t cur_time = time(NULL); struct dirent *ep; struct stat statbuf; double size = 0; double maxsz = 0; while ((ep = readdir(dp)) != NULL) { if (dot_or_dotdot(ep->d_name)) continue; char *dname = concat_path_file(pPath, ep->d_name); if (lstat(dname, &statbuf) != 0) { goto next; } if (S_ISDIR(statbuf.st_mode)) { double sz = get_dirsize(dname); size += sz; if (worst_dir && (!excluded || strcmp(excluded, ep->d_name) != 0)) { /* Calculate "weighted" size and age * w = sz_kbytes * age_mins */ sz /= 1024; long age = (cur_time - statbuf.st_mtime) / 60; if (age > 0) sz *= age; if (sz > maxsz) { if (!this_is_a_dd(dname)) { VERB1 log("'%s' isn't a problem directory, probably a stray directory?", dname); } else { maxsz = sz; free(*worst_dir); *worst_dir = xstrdup(ep->d_name); } } } } else if (S_ISREG(statbuf.st_mode)) { size += statbuf.st_size; } next: free(dname); } closedir(dp); return size; }
static int is_crash_a_dup(const char *dump_dir_name, void *param) { struct cdump_state *state = (struct cdump_state *)param; if (state->uuid) return 0; /* we already checked it, don't do it again */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return 0; /* wtf? (error, but will be handled elsewhere later) */ state->uuid = dd_load_text_ext(dd, FILENAME_UUID, DD_FAIL_QUIETLY_ENOENT + DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE ); dd_close(dd); if (!state->uuid) { return 0; /* no uuid (yet), "run_event, please continue iterating" */ } /* Scan crash dumps looking for a dup */ //TODO: explain why this is safe wrt concurrent runs DIR *dir = opendir(DEBUG_DUMPS_DIR); if (dir != NULL) { struct dirent *dent; while ((dent = readdir(dir)) != NULL) { if (dot_or_dotdot(dent->d_name)) continue; /* skip "." and ".." */ int different; char *uid, *uuid; char *dump_dir_name2 = concat_path_file(DEBUG_DUMPS_DIR, dent->d_name); if (strcmp(dump_dir_name, dump_dir_name2) == 0) goto next; /* we are never a dup of ourself */ dd = dd_opendir(dump_dir_name2, /*flags:*/ DD_FAIL_QUIETLY_ENOENT); if (!dd) goto next; uid = dd_load_text(dd, FILENAME_UID); uuid = dd_load_text(dd, FILENAME_UUID); dd_close(dd); different = strcmp(state->uid, uid) || strcmp(state->uuid, uuid); free(uid); free(uuid); if (different) goto next; state->crash_dump_dup_name = dump_dir_name2; /* "run_event, please stop iterating": */ return 1; next: free(dump_dir_name2); } closedir(dir); } /* No dup found */ return 0; /* "run_event, please continue iterating" */ }