static int clean_posix_shm_internal(DIR *dir, uid_t uid) { struct dirent *de; int ret = 0, r; assert(dir); FOREACH_DIRENT(de, dir, goto fail) { struct stat st; if (STR_IN_SET(de->d_name, "..", ".")) continue; if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { if (errno == ENOENT) continue; log_warning("Failed to stat() POSIX shared memory segment %s: %m", de->d_name); ret = -errno; continue; } if (st.st_uid != uid) continue; if (S_ISDIR(st.st_mode)) { _cleanup_closedir_ DIR *kid; kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); if (!kid) { if (errno != ENOENT) { log_warning("Failed to enter shared memory directory %s: %m", de->d_name); ret = -errno; } } else { r = clean_posix_shm_internal(kid, uid); if (r < 0) ret = r; } if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) { if (errno == ENOENT) continue; log_warning("Failed to remove POSIX shared memory directory %s: %m", de->d_name); ret = -errno; } } else { if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { if (errno == ENOENT) continue; log_warning("Failed to remove POSIX shared memory segment %s: %m", de->d_name); ret = -errno; } } } return ret; fail: log_warning("Failed to read /dev/shm: %m"); return -errno; }
static int dir_cleanup( const char *p, DIR *d, const struct stat *ds, usec_t cutoff, dev_t rootdev, bool mountpoint, int maxdepth, bool keep_this_level) { struct dirent *dent; struct timespec times[2]; bool deleted = false; char *sub_path = NULL; int r = 0; while ((dent = readdir(d))) { struct stat s; usec_t age; if (streq(dent->d_name, ".") || streq(dent->d_name, "..")) continue; if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { if (errno != ENOENT) { log_error("stat(%s/%s) failed: %m", p, dent->d_name); r = -errno; } continue; } /* Stay on the same filesystem */ if (s.st_dev != rootdev) continue; /* Do not delete read-only files owned by root */ if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) continue; free(sub_path); sub_path = NULL; if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) { log_error("Out of memory"); r = -ENOMEM; goto finish; } /* Is there an item configured for this path? */ if (hashmap_get(items, sub_path)) continue; if (find_glob(globs, sub_path)) continue; if (S_ISDIR(s.st_mode)) { if (mountpoint && streq(dent->d_name, "lost+found") && s.st_uid == 0) continue; if (maxdepth <= 0) log_warning("Reached max depth on %s.", sub_path); else { DIR *sub_dir; int q; sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME); if (sub_dir == NULL) { if (errno != ENOENT) { log_error("opendir(%s/%s) failed: %m", p, dent->d_name); r = -errno; } continue; } q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); closedir(sub_dir); if (q < 0) r = q; } /* Note: if you are wondering why we don't * support the sticky bit for excluding * directories from cleaning like we do it for * other file system objects: well, the sticky * bit already has a meaning for directories, * so we don't want to overload that. */ if (keep_this_level) continue; /* Ignore ctime, we change it when deleting */ age = MAX(timespec_load(&s.st_mtim), timespec_load(&s.st_atim)); if (age >= cutoff) continue; log_debug("rmdir '%s'\n", sub_path); if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) { if (errno != ENOENT && errno != ENOTEMPTY) { log_error("rmdir(%s): %m", sub_path); r = -errno; } } } else { /* Skip files for which the sticky bit is * set. These are semantics we define, and are * unknown elsewhere. See XDG_RUNTIME_DIR * specification for details. */ if (s.st_mode & S_ISVTX) continue; if (mountpoint && S_ISREG(s.st_mode)) { if (streq(dent->d_name, ".journal") && s.st_uid == 0) continue; if (streq(dent->d_name, "aquota.user") || streq(dent->d_name, "aquota.group")) continue; } /* Ignore sockets that are listed in /proc/net/unix */ if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) continue; /* Ignore device nodes */ if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) continue; /* Keep files on this level around if this is * requested */ if (keep_this_level) continue; age = MAX3(timespec_load(&s.st_mtim), timespec_load(&s.st_atim), timespec_load(&s.st_ctim)); if (age >= cutoff) continue; log_debug("unlink '%s'\n", sub_path); if (unlinkat(dirfd(d), dent->d_name, 0) < 0) { if (errno != ENOENT) { log_error("unlink(%s): %m", sub_path); r = -errno; } } deleted = true; } } finish: if (deleted) { /* Restore original directory timestamps */ times[0] = ds->st_atim; times[1] = ds->st_mtim; if (futimens(dirfd(d), times) < 0) log_error("utimensat(%s): %m", p); } free(sub_path); return r; }