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; }
static bool rm_mounts_create_tables(RmMountTable *self) { /* partition dev_t to disk dev_t */ self->part_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rm_part_info_free); /* disk dev_t to boolean indication if disk is rotational */ self->disk_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rm_disk_info_free); self->nfs_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* Mapping dev_t => true (used as set) */ self->evilfs_table = g_hash_table_new(NULL, NULL); RmMountEntry *entry = NULL; RmMountEntries *mnt_entries = rm_mount_list_open(self); if(mnt_entries == NULL) { return false; } while((entry = rm_mount_list_next(mnt_entries))) { RmStat stat_buf_folder; if(rm_sys_stat(entry->dir, &stat_buf_folder) == -1) { continue; } dev_t whole_disk = 0; gchar is_rotational = true; char diskname[PATH_MAX]; memset(diskname, 0, sizeof(diskname)); RmStat stat_buf_dev; if(rm_sys_stat(entry->fsname, &stat_buf_dev) == -1) { char *nfs_marker = NULL; /* folder rm_sys_stat() is ok but devname rm_sys_stat() is not; this happens for example * with tmpfs and with nfs mounts. Try to handle a few such cases. * */ if(rm_mounts_is_ramdisk(entry->fsname)) { strncpy(diskname, entry->fsname, sizeof(diskname)); is_rotational = false; whole_disk = stat_buf_folder.st_dev; } else if((nfs_marker = strstr(entry->fsname, ":/")) != NULL) { size_t until_slash = MIN((int)sizeof(entry->fsname), nfs_marker - entry->fsname); strncpy(diskname, entry->fsname, until_slash); is_rotational = true; /* Assign different dev ids (with major id 0) to different nfs servers */ if(!g_hash_table_contains(self->nfs_table, diskname)) { g_hash_table_insert(self->nfs_table, g_strdup(diskname), NULL); } whole_disk = makedev(0, g_hash_table_size(self->nfs_table)); } else { strncpy(diskname, "unknown", sizeof(diskname)); is_rotational = true; whole_disk = 0; } } else { if(rm_mounts_devno_to_wholedisk( entry, stat_buf_dev.st_rdev, diskname, sizeof(diskname), &whole_disk ) == -1) { /* folder and devname rm_sys_stat() are ok but blkid failed; this happens when? * Treat as a non-rotational device using devname dev as whole_disk key * */ rm_log_debug(RED"devno_to_wholedisk failed for %s\n"RESET, entry->fsname); whole_disk = stat_buf_dev.st_dev; strncpy(diskname, entry->fsname, sizeof(diskname)); is_rotational = false; } else { is_rotational = rm_mounts_is_rotational_blockdev(diskname); } } g_hash_table_insert( self->part_table, GUINT_TO_POINTER(stat_buf_folder.st_dev), rm_part_info_new (entry->dir, whole_disk)); /* small hack, so also the full disk id can be given to the api below */ if (!g_hash_table_contains(self->part_table, GINT_TO_POINTER(whole_disk))) { g_hash_table_insert( self->part_table, GUINT_TO_POINTER(whole_disk), rm_part_info_new (entry->dir, whole_disk)); } if (!g_hash_table_contains(self->disk_table, GINT_TO_POINTER(whole_disk))) { g_hash_table_insert( self->disk_table, GINT_TO_POINTER(whole_disk), rm_disk_info_new(diskname, is_rotational)); } rm_log_info("%02u:%02u %50s -> %02u:%02u %-12s (underlying disk: %s; rotational: %3s\n)", major(stat_buf_folder.st_dev), minor(stat_buf_folder.st_dev), entry->dir, major(whole_disk), minor(whole_disk), entry->fsname, diskname, is_rotational ? "yes" : "no" ); } #if HAVE_SYSCTL if(DISK_TABLE) { g_hash_table_unref(DISK_TABLE); } #endif rm_mount_list_close(mnt_entries); return true; }
static RmMountEntries *rm_mount_list_open(RmMountTable *table) { RmMountEntries *self = g_slice_new(RmMountEntries); self->mnt_entries = g_unix_mounts_get(NULL); self->entries = NULL; self->current = NULL; for(GList *iter = self->mnt_entries; iter; iter = iter->next) { RmMountEntry *wrap_entry = g_slice_new(RmMountEntry); GUnixMountEntry *entry = iter->data; wrap_entry->fsname = g_strdup(g_unix_mount_get_device_path(entry)); wrap_entry->dir = g_strdup(g_unix_mount_get_mount_path(entry)); wrap_entry->type = g_strdup(g_unix_mount_get_fs_type(entry)); self->entries = g_list_prepend(self->entries, wrap_entry); } 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. * * Also ignore kernel filesystems. * * So better go and ignore it. */ static struct RmEvilFs { /* fsname as show by `mount` */ const char *name; /* Wether to warn about the exclusion on this */ bool unusual; } evilfs_types[] = {{"bindfs", 1}, {"nullfs", 1}, /* Ignore the usual linux file system spam */ {"proc", 0}, {"cgroup", 0}, {"configfs", 0}, {"sys", 0}, {"devtmpfs", 0}, {"debugfs", 0}, {NULL, 0}}; /* btrfs and ocfs2 filesystems support reflinks for deduplication */ static const char *reflinkfs_types[] = {"btrfs", "ocfs2", NULL}; const struct RmEvilFs *evilfs_found = NULL; for(int i = 0; evilfs_types[i].name && !evilfs_found; ++i) { if(strcmp(evilfs_types[i].name, wrap_entry->type) == 0) { evilfs_found = &evilfs_types[i]; } } const char *reflinkfs_found = NULL; for(int i = 0; reflinkfs_types[i] && !reflinkfs_found; ++i) { if(strcmp(reflinkfs_types[i], wrap_entry->type) == 0) { reflinkfs_found = reflinkfs_types[i]; break; } } 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)); GLogLevelFlags log_level = G_LOG_LEVEL_DEBUG; if(evilfs_found->unusual) { log_level = G_LOG_LEVEL_WARNING; rm_log_warning_prefix(); } else { rm_log_debug_prefix(); } g_log("rmlint", log_level, _("`%s` mount detected at %s (#%u); Ignoring all files in it.\n"), evilfs_found->name, wrap_entry->dir, (unsigned)dir_stat.st_dev); } rm_log_debug_line("Filesystem %s: %s", wrap_entry->dir, (reflinkfs_found) ? "reflink" : "normal"); if(reflinkfs_found != NULL) { RmStat dir_stat; rm_sys_stat(wrap_entry->dir, &dir_stat); g_hash_table_insert(table->reflinkfs_table, GUINT_TO_POINTER(dir_stat.st_dev), (gpointer)reflinkfs_found); } } return self; }