void rm_tm_feed(RmTreeMerger *self, RmFile *file) { RM_DEFINE_PATH(file); char *dirname = g_path_get_dirname(file_path); /* See if we know that directory already */ RmDirectory *directory = rm_trie_search(&self->dir_tree, dirname); if(directory == NULL) { /* Get the actual file count */ int file_count = GPOINTER_TO_INT(rm_trie_search(&self->count_tree, dirname)); if(file_count == 0) { rm_log_error( RED "Empty directory or weird RmFile encountered; rejecting.\n" RESET); file_count = -1; } directory = rm_directory_new(dirname); directory->file_count = file_count; /* Make the new directory known */ rm_trie_insert(&self->dir_tree, dirname, directory); g_queue_push_head(&self->valid_dirs, directory); } else { g_free(dirname); } g_queue_push_tail(self->free_list, file); rm_directory_add(directory, file); /* Add the file to this directory */ g_queue_push_head(&directory->known_files, file); /* Remember the digest (if only to free it later...) */ g_hash_table_replace(self->known_hashs, file->digest, NULL); /* Check if the directory reached the number of actual files in it */ if(directory->dupe_count == directory->file_count && directory->file_count > 0) { rm_tm_insert_dir(self, directory); } }
static bool rm_tm_count_files(RmTrie *count_tree, char **paths, RmSession *session) { if(*paths == NULL) { rm_log_error("No paths passed to rm_tm_count_files\n"); return false; } int fts_flags = FTS_COMFOLLOW; if(session->cfg->follow_symlinks) { fts_flags |= FTS_LOGICAL; } else { fts_flags |= FTS_PHYSICAL; } /* This tree stores the full file paths. It is joined into a full directory tree later. */ RmTrie file_tree; rm_trie_init(&file_tree); FTS *fts = fts_open(paths, fts_flags, NULL); if(fts == NULL) { rm_log_perror("fts_open failed"); return false; } FTSENT *ent = NULL; while((ent = fts_read(fts))) { /* Handle large files (where fts fails with FTS_NS) */ if(ent->fts_info == FTS_NS) { RmStat stat_buf; if(rm_sys_stat(ent->fts_path, &stat_buf) == -1) { rm_log_perror("stat(2) failed"); continue; } else { /* Must be a large file (or followed link to it) */ ent->fts_info = FTS_F; } } switch(ent->fts_info) { case FTS_ERR: case FTS_DC: /* Save this path as an error */ rm_trie_insert(&file_tree, ent->fts_path, GINT_TO_POINTER(true)); break; case FTS_F: case FTS_SL: case FTS_NS: case FTS_SLNONE: case FTS_DEFAULT: /* Save this path as countable file */ if(ent->fts_statp->st_size > 0) { rm_trie_insert(&file_tree, ent->fts_path, GINT_TO_POINTER(false)); } case FTS_D: case FTS_DNR: case FTS_DOT: case FTS_DP: case FTS_NSOK: default: /* other fts states, that do not count as errors or files */ break; } } if(fts_close(fts) != 0) { rm_log_perror("fts_close failed"); return false; } rm_trie_iter(&file_tree, NULL, true, false, rm_tm_count_art_callback, count_tree); /* Now flag everything as a no-go over the given paths, * otherwise we would continue merging till / with fatal consequences, * since / does not have more files as paths[0] */ for(int i = 0; paths[i]; ++i) { /* Just call the callback directly */ RmNode *node = rm_trie_search_node(&file_tree, paths[i]); if(node != NULL) { node->data = GINT_TO_POINTER(true); rm_tm_count_art_callback(&file_tree, node, 0, count_tree); } } #ifdef _RM_TREEMERGE_DEBUG rm_trie_print(count_tree); #endif rm_trie_destroy(&file_tree); return true; }
static RmMountEntries *rm_mount_list_open(RmMountTable *table) { RmMountEntries *self = g_slice_new(RmMountEntries); self->entries = NULL; self->current = NULL; #if HAVE_GETMNTENT struct mntent *entry = NULL; self->mnt_ent_file = setmntent("/etc/mtab", "r"); if(self->mnt_ent_file != NULL) { while((entry = getmntent(self->mnt_ent_file))) { RmMountEntry *wrap_entry = g_slice_new(RmMountEntry); wrap_entry->fsname = g_strdup(entry->mnt_fsname); wrap_entry->dir = g_strdup(entry->mnt_dir); self->entries = g_list_prepend(self->entries, wrap_entry); } endmntent(self->mnt_ent_file); } else { rm_log_perror("getmntent"); } #elif HAVE_GETMNTINFO /* probably FreeBSD or other */ int mnt_list_n = 0; struct statfs *mnt_list = NULL; if((mnt_list_n = getmntinfo(&mnt_list, MNT_NOWAIT)) != 0) { for(int i = 0; i < mnt_list_n; ++i) { RmMountEntry *wrap_entry = g_slice_new(RmMountEntry); struct statfs *entry = &mnt_list[i]; wrap_entry->fsname = g_strdup(entry->f_mntfromname); wrap_entry->dir = g_strdup(entry->f_mntonname); self->entries = g_list_prepend(self->entries, wrap_entry); } } else { rm_log_perror("getmntinfo"); } #endif RmMountEntry *wrap_entry = NULL; while((wrap_entry = rm_mount_list_next(self))) { /* bindfs mounts mirror directory trees. * This cannot be detected properly by rmlint since * files in it have the same inode as their unmirrored file, but * a different dev_t. * * So better go and ignore it. */ static const char *evilfs_types[] = {"bindfs", "nullfs", NULL}; const char *evilfs_found = NULL; for(int i = 0; evilfs_types[i] && !evilfs_found; ++i) { if(strcmp(evilfs_types[i], wrap_entry->fsname) == 0) { evilfs_found = evilfs_types[i]; } } if(evilfs_found != NULL) { RmStat dir_stat; rm_sys_stat(wrap_entry->dir, &dir_stat); g_hash_table_insert( table->evilfs_table, GUINT_TO_POINTER(dir_stat.st_dev), GUINT_TO_POINTER(1) ); rm_log_error( YELLOW"WARNING:"RESET" `%s` mount detected at %s (#%u); Ignoring all files in it.\n", evilfs_found, wrap_entry->dir, (unsigned)dir_stat.st_dev ); } } return self; }