+ 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 (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);
+ 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);
+ closedir(sub_dir);
+
+ if (q < 0)
+ r = q;
+ }
+
+ /* 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 {
+ if (mountpoint) {
+ 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;
+ }
+
+ 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;
+}
+
+static int clean_item(Item *i) {
+ DIR *d;
+ struct stat s, ps;
+ bool mountpoint;
+ int r;
+ usec_t cutoff, n;
+
+ assert(i);
+
+ if (i->type != CREATE_DIRECTORY &&
+ i->type != TRUNCATE_DIRECTORY &&
+ i->type != IGNORE_PATH)
+ return 0;
+
+ if (!i->age_set || i->age <= 0)
+ return 0;
+
+ n = now(CLOCK_REALTIME);
+ if (n < i->age)
+ return 0;
+
+ cutoff = n - i->age;
+
+ d = opendir(i->path);
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open directory %s: %m", i->path);
+ return -errno;
+ }
+
+ if (fstat(dirfd(d), &s) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ r = -errno;