tmpfiles.c revision 657cf7f4f8d376e082db48022d2be193ff647d06
df8bdeb362277e8d95a74d6c097341fe97409948johnz/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz/***
df8bdeb362277e8d95a74d6c097341fe97409948johnz This file is part of systemd.
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz Copyright 2010 Lennart Poettering, Kay Sievers
df8bdeb362277e8d95a74d6c097341fe97409948johnz Copyright 2015 Zbigniew Jędrzejewski-Szmek
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz systemd is free software; you can redistribute it and/or modify it
df8bdeb362277e8d95a74d6c097341fe97409948johnz under the terms of the GNU Lesser General Public License as published by
df8bdeb362277e8d95a74d6c097341fe97409948johnz the Free Software Foundation; either version 2.1 of the License, or
df8bdeb362277e8d95a74d6c097341fe97409948johnz (at your option) any later version.
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz systemd is distributed in the hope that it will be useful, but
df8bdeb362277e8d95a74d6c097341fe97409948johnz WITHOUT ANY WARRANTY; without even the implied warranty of
df8bdeb362277e8d95a74d6c097341fe97409948johnz MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
df8bdeb362277e8d95a74d6c097341fe97409948johnz Lesser General Public License for more details.
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz You should have received a copy of the GNU Lesser General Public License
df8bdeb362277e8d95a74d6c097341fe97409948johnz along with systemd; If not, see <http://www.gnu.org/licenses/>.
df8bdeb362277e8d95a74d6c097341fe97409948johnz***/
df8bdeb362277e8d95a74d6c097341fe97409948johnz
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll#include <unistd.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <fcntl.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <errno.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <string.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <limits.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <dirent.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <stdio.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <stdlib.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <stddef.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <getopt.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <stdbool.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <time.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <glob.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <fnmatch.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <sys/stat.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include <sys/xattr.h>
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "log.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "util.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "macro.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "missing.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "mkdir.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "path-util.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "strv.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "label.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "set.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "conf-files.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "capability.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "specifier.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "build.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "copy.h"
df8bdeb362277e8d95a74d6c097341fe97409948johnz#include "selinux-util.h"
735564919188238196dbd0d320770dda59b38369Anthony Scarpino#include "btrfs-util.h"
735564919188238196dbd0d320770dda59b38369Anthony Scarpino#include "acl-util.h"
735564919188238196dbd0d320770dda59b38369Anthony Scarpino
735564919188238196dbd0d320770dda59b38369Anthony Scarpino/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
df8bdeb362277e8d95a74d6c097341fe97409948johnz * them in the file system. This is intended to be used to create
df8bdeb362277e8d95a74d6c097341fe97409948johnz * properly owned directories beneath /tmp, /var/tmp, /run, which are
df8bdeb362277e8d95a74d6c097341fe97409948johnz * volatile and hence need to be recreated on bootup. */
735564919188238196dbd0d320770dda59b38369Anthony Scarpino
df8bdeb362277e8d95a74d6c097341fe97409948johnztypedef enum ItemType {
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* These ones take file names */
735564919188238196dbd0d320770dda59b38369Anthony Scarpino CREATE_FILE = 'f',
df8bdeb362277e8d95a74d6c097341fe97409948johnz TRUNCATE_FILE = 'F',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_DIRECTORY = 'd',
df8bdeb362277e8d95a74d6c097341fe97409948johnz TRUNCATE_DIRECTORY = 'D',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_SUBVOLUME = 'v',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_FIFO = 'p',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_SYMLINK = 'L',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_CHAR_DEVICE = 'c',
df8bdeb362277e8d95a74d6c097341fe97409948johnz CREATE_BLOCK_DEVICE = 'b',
df8bdeb362277e8d95a74d6c097341fe97409948johnz COPY_FILES = 'C',
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* These ones take globs */
df8bdeb362277e8d95a74d6c097341fe97409948johnz SET_XATTR = 't',
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_SET_XATTR = 'T',
df8bdeb362277e8d95a74d6c097341fe97409948johnz SET_ACL = 'a',
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_SET_ACL = 'A',
df8bdeb362277e8d95a74d6c097341fe97409948johnz WRITE_FILE = 'w',
df8bdeb362277e8d95a74d6c097341fe97409948johnz IGNORE_PATH = 'x',
df8bdeb362277e8d95a74d6c097341fe97409948johnz IGNORE_DIRECTORY_PATH = 'X',
df8bdeb362277e8d95a74d6c097341fe97409948johnz REMOVE_PATH = 'r',
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_REMOVE_PATH = 'R',
df8bdeb362277e8d95a74d6c097341fe97409948johnz ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
df8bdeb362277e8d95a74d6c097341fe97409948johnz RELABEL_PATH = 'z',
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_RELABEL_PATH = 'Z',
df8bdeb362277e8d95a74d6c097341fe97409948johnz} ItemType;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMtypedef struct Item {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM ItemType type;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz char *path;
df8bdeb362277e8d95a74d6c097341fe97409948johnz char *argument;
df8bdeb362277e8d95a74d6c097341fe97409948johnz char **xattrs;
df8bdeb362277e8d95a74d6c097341fe97409948johnz#ifdef HAVE_ACL
df8bdeb362277e8d95a74d6c097341fe97409948johnz acl_t acl_access;
df8bdeb362277e8d95a74d6c097341fe97409948johnz acl_t acl_default;
df8bdeb362277e8d95a74d6c097341fe97409948johnz#endif
df8bdeb362277e8d95a74d6c097341fe97409948johnz uid_t uid;
df8bdeb362277e8d95a74d6c097341fe97409948johnz gid_t gid;
df8bdeb362277e8d95a74d6c097341fe97409948johnz mode_t mode;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino usec_t age;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz dev_t major_minor;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool uid_set:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino bool gid_set:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino bool mode_set:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino bool age_set:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino bool mask_perms:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino
735564919188238196dbd0d320770dda59b38369Anthony Scarpino bool keep_first_level:1;
735564919188238196dbd0d320770dda59b38369Anthony Scarpino
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool force:1;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool done:1;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM} Item;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMtypedef struct ItemArray {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM Item *items;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM size_t count;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM size_t size;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM} ItemArray;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic bool arg_create = false;
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic bool arg_clean = false;
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic bool arg_remove = false;
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic bool arg_boot = false;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic char **arg_include_prefixes = NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic char **arg_exclude_prefixes = NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic char *arg_root = NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM#define MAX_DEPTH 256
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic Hashmap *items = NULL, *globs = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic Set *unix_sockets = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic bool needs_glob(ItemType t) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return IN_SET(t,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM WRITE_FILE,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM IGNORE_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz IGNORE_DIRECTORY_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz REMOVE_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_REMOVE_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz ADJUST_MODE,
df8bdeb362277e8d95a74d6c097341fe97409948johnz RELABEL_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_RELABEL_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz SET_XATTR,
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_SET_XATTR,
df8bdeb362277e8d95a74d6c097341fe97409948johnz SET_ACL,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino RECURSIVE_SET_ACL);
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll}
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersollstatic bool takes_ownership(ItemType t) {
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll return IN_SET(t,
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll CREATE_FILE,
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll TRUNCATE_FILE,
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll CREATE_DIRECTORY,
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll TRUNCATE_DIRECTORY,
9f0bc604621fbb9b9b038e6de7da8f9c46e28608Wyllys Ingersoll CREATE_SUBVOLUME,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino CREATE_FIFO,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino CREATE_SYMLINK,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino CREATE_CHAR_DEVICE,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino CREATE_BLOCK_DEVICE,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino COPY_FILES,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino
735564919188238196dbd0d320770dda59b38369Anthony Scarpino WRITE_FILE,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino IGNORE_PATH,
735564919188238196dbd0d320770dda59b38369Anthony Scarpino IGNORE_DIRECTORY_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz REMOVE_PATH,
df8bdeb362277e8d95a74d6c097341fe97409948johnz RECURSIVE_REMOVE_PATH);
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic struct Item* find_glob(Hashmap *h, const char *match) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz ItemArray *j;
df8bdeb362277e8d95a74d6c097341fe97409948johnz Iterator i;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz HASHMAP_FOREACH(j, h, i) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz unsigned n;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz for (n = 0; n < j->count; n++) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz Item *item = j->items + n;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return item;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz return NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic void load_unix_sockets(void) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz _cleanup_fclose_ FILE *f = NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnz char line[LINE_MAX];
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (unix_sockets)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* We maintain a cache of the sockets we found in
df8bdeb362277e8d95a74d6c097341fe97409948johnz * /proc/net/unix to speed things up a little. */
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz unix_sockets = set_new(&string_hash_ops);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!unix_sockets)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz f = fopen("/proc/net/unix", "re");
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!f)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Skip header */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!fgets(line, sizeof(line), f))
df8bdeb362277e8d95a74d6c097341fe97409948johnz goto fail;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz for (;;) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz char *p, *s;
df8bdeb362277e8d95a74d6c097341fe97409948johnz int k;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!fgets(line, sizeof(line), f))
df8bdeb362277e8d95a74d6c097341fe97409948johnz break;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz truncate_nl(line);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz p = strchr(line, ':');
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!p)
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (strlen(p) < 37)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM p += 37;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM p += strspn(p, WHITESPACE);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM p += strcspn(p, WHITESPACE); /* skip one more word */
df8bdeb362277e8d95a74d6c097341fe97409948johnz p += strspn(p, WHITESPACE);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (*p != '/')
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM s = strdup(p);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (!s)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM goto fail;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM path_kill_slashes(s);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM k = set_consume(unix_sockets, s);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (k < 0 && k != -EEXIST)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM goto fail;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMfail:
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM set_free_free(unix_sockets);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM unix_sockets = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM}
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic bool unix_socket_alive(const char *fn) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(fn);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz load_unix_sockets();
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (unix_sockets)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return !!set_get(unix_sockets, (char*) fn);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* We don't know, so assume yes */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return true;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM}
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic int dir_is_mount_point(DIR *d, const char *subdir) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM union file_handle_union h = FILE_HANDLE_INIT;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM int mount_id_parent, mount_id;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM int r_p, r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r_p < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz r_p = -errno;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz h.handle.handle_bytes = MAX_HANDLE_SZ;
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = -errno;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* got no handle; make no assumptions, return error */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r_p < 0 && r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r_p;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* got both handles; if they differ, it is a mount point */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r_p >= 0 && r >= 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return mount_id_parent != mount_id;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
2225707c7e7edf7c636ed349df2592ef85329cddValerie Bubb Fenwick /* got only one handle; assume different mount points if one
df8bdeb362277e8d95a74d6c097341fe97409948johnz * of both queries was not supported by the filesystem */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return true;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* return error */
735564919188238196dbd0d320770dda59b38369Anthony Scarpino if (r_p < 0)
735564919188238196dbd0d320770dda59b38369Anthony Scarpino return r_p;
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic DIR* xopendirat_nomod(int dirfd, const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz DIR *dir;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!dir) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
df8bdeb362277e8d95a74d6c097341fe97409948johnz dirfd == AT_FDCWD ? "" : "sub", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (errno == EPERM) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz dir = xopendirat(dirfd, path, O_NOFOLLOW);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!dir)
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
df8bdeb362277e8d95a74d6c097341fe97409948johnz dirfd == AT_FDCWD ? "" : "sub", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz return dir;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic DIR* opendir_nomod(const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz return xopendirat_nomod(AT_FDCWD, path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int dir_cleanup(
df8bdeb362277e8d95a74d6c097341fe97409948johnz Item *i,
df8bdeb362277e8d95a74d6c097341fe97409948johnz const char *p,
df8bdeb362277e8d95a74d6c097341fe97409948johnz DIR *d,
df8bdeb362277e8d95a74d6c097341fe97409948johnz const struct stat *ds,
df8bdeb362277e8d95a74d6c097341fe97409948johnz usec_t cutoff,
df8bdeb362277e8d95a74d6c097341fe97409948johnz dev_t rootdev,
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool mountpoint,
df8bdeb362277e8d95a74d6c097341fe97409948johnz int maxdepth,
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool keep_this_level) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM struct dirent *dent;
df8bdeb362277e8d95a74d6c097341fe97409948johnz struct timespec times[2];
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM bool deleted = false;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM int r = 0;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM while ((dent = readdir(d))) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM struct stat s;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM usec_t age;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM _cleanup_free_ char *sub_path = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (STR_IN_SET(dent->d_name, ".", ".."))
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (errno == ENOENT)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* FUSE, NFS mounts, SELinux might return EACCES */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (errno == EACCES)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM else
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = -errno;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Stay on the same filesystem */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (s.st_dev != rootdev) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Try to detect bind mounts of the same filesystem instance; they
df8bdeb362277e8d95a74d6c097341fe97409948johnz * do not differ in device major/minors. This type of query is not
df8bdeb362277e8d95a74d6c097341fe97409948johnz * supported on all kernels or filesystem types though. */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
df8bdeb362277e8d95a74d6c097341fe97409948johnz p, dent->d_name);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Do not delete read-only files owned by root */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz sub_path = strjoin(p, "/", dent->d_name, NULL);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!sub_path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = log_oom();
df8bdeb362277e8d95a74d6c097341fe97409948johnz goto finish;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Is there an item configured for this path? */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (hashmap_get(items, sub_path)) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (find_glob(globs, sub_path)) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (S_ISDIR(s.st_mode)) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (mountpoint &&
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM streq(dent->d_name, "lost+found") &&
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM s.st_uid == 0) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Ignoring \"%s\".", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (maxdepth <= 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_warning("Reached max depth on \"%s\".", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz else {
df8bdeb362277e8d95a74d6c097341fe97409948johnz _cleanup_closedir_ DIR *sub_dir;
df8bdeb362277e8d95a74d6c097341fe97409948johnz int q;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (!sub_dir) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (errno != ENOENT)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (q < 0)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = q;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Note: if you are wondering why we don't
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * support the sticky bit for excluding
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * directories from cleaning like we do it for
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * other file system objects: well, the sticky
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * bit already has a meaning for directories,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * so we don't want to overload that. */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (keep_this_level) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Keeping \"%s\".", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Ignore ctime, we change it when deleting */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM age = timespec_load(&s.st_mtim);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (age >= cutoff) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM char a[FORMAT_TIMESTAMP_MAX];
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Follows spelling in stat(1). */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Directory \"%s\": modify time %s is too new.",
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM sub_path,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM format_timestamp_us(a, sizeof(a), age));
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM age = timespec_load(&s.st_atim);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (age >= cutoff) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM char a[FORMAT_TIMESTAMP_MAX];
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Directory \"%s\": access time %s is too new.",
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM sub_path,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM format_timestamp_us(a, sizeof(a), age));
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Removing directory \"%s\".", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (errno != ENOENT && errno != ENOTEMPTY) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_error_errno(errno, "rmdir(%s): %m", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = -errno;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz } else {
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Skip files for which the sticky bit is
df8bdeb362277e8d95a74d6c097341fe97409948johnz * set. These are semantics we define, and are
df8bdeb362277e8d95a74d6c097341fe97409948johnz * unknown elsewhere. See XDG_RUNTIME_DIR
df8bdeb362277e8d95a74d6c097341fe97409948johnz * specification for details. */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (s.st_mode & S_ISVTX) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Skipping \"%s\": sticky bit set.", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (mountpoint && S_ISREG(s.st_mode))
df8bdeb362277e8d95a74d6c097341fe97409948johnz if ((streq(dent->d_name, ".journal") && s.st_uid == 0) ||
df8bdeb362277e8d95a74d6c097341fe97409948johnz streq(dent->d_name, "aquota.user") ||
df8bdeb362277e8d95a74d6c097341fe97409948johnz streq(dent->d_name, "aquota.group")) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Skipping \"%s\".", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Ignore sockets that are listed in /proc/net/unix */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Skipping \"%s\": live socket.", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Ignore device nodes */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("Skipping \"%s\": a device.", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Keep files on this level around if this is
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * requested */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (keep_this_level) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Keeping \"%s\".", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz age = timespec_load(&s.st_mtim);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (age >= cutoff) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz char a[FORMAT_TIMESTAMP_MAX];
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* Follows spelling in stat(1). */
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("File \"%s\": modify time %s is too new.",
df8bdeb362277e8d95a74d6c097341fe97409948johnz sub_path,
df8bdeb362277e8d95a74d6c097341fe97409948johnz format_timestamp_us(a, sizeof(a), age));
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz age = timespec_load(&s.st_atim);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (age >= cutoff) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz char a[FORMAT_TIMESTAMP_MAX];
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("File \"%s\": access time %s is too new.",
df8bdeb362277e8d95a74d6c097341fe97409948johnz sub_path,
df8bdeb362277e8d95a74d6c097341fe97409948johnz format_timestamp_us(a, sizeof(a), age));
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz age = timespec_load(&s.st_ctim);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (age >= cutoff) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz char a[FORMAT_TIMESTAMP_MAX];
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("File \"%s\": change time %s is too new.",
df8bdeb362277e8d95a74d6c097341fe97409948johnz sub_path,
df8bdeb362277e8d95a74d6c097341fe97409948johnz format_timestamp_us(a, sizeof(a), age));
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("unlink \"%s\"", sub_path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (errno != ENOENT)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = log_error_errno(errno, "unlink(%s): %m", sub_path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM deleted = true;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMfinish:
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (deleted) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM usec_t age1, age2;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Restore original directory timestamps */
df8bdeb362277e8d95a74d6c097341fe97409948johnz times[0] = ds->st_atim;
df8bdeb362277e8d95a74d6c097341fe97409948johnz times[1] = ds->st_mtim;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM age1 = timespec_load(&ds->st_atim);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM age2 = timespec_load(&ds->st_mtim);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_debug("Restoring access and modification time on \"%s\": %s, %s",
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM p,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM format_timestamp_us(a, sizeof(a), age1),
df8bdeb362277e8d95a74d6c097341fe97409948johnz format_timestamp_us(b, sizeof(b), age2));
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (futimens(dirfd(d), times) < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_error_errno(errno, "utimensat(%s): %m", p);
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int path_set_perms(Item *i, const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz struct stat st;
df8bdeb362277e8d95a74d6c097341fe97409948johnz bool st_valid;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(i);
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz st_valid = stat(path, &st) == 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* not using i->path directly because it may be a glob */
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (i->mode_set) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz mode_t m = i->mode;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (i->mask_perms && st_valid) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!(st.st_mode & 0111))
df8bdeb362277e8d95a74d6c097341fe97409948johnz m &= ~0111;
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!(st.st_mode & 0222))
df8bdeb362277e8d95a74d6c097341fe97409948johnz m &= ~0222;
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!(st.st_mode & 0444))
df8bdeb362277e8d95a74d6c097341fe97409948johnz m &= ~0444;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (!S_ISDIR(st.st_mode))
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (st_valid && m == (st.st_mode & 07777))
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("\"%s\" has right mode %o", path, st.st_mode);
df8bdeb362277e8d95a74d6c097341fe97409948johnz else {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("chmod \"%s\" to mode %o", path, m);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (chmod(path, m) < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return log_error_errno(errno, "chmod(%s) failed: %m", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if ((!st_valid || i->uid != st.st_uid || i->gid != st.st_gid) &&
df8bdeb362277e8d95a74d6c097341fe97409948johnz (i->uid_set || i->gid_set)) {
2225707c7e7edf7c636ed349df2592ef85329cddValerie Bubb Fenwick log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
2225707c7e7edf7c636ed349df2592ef85329cddValerie Bubb Fenwick path,
2225707c7e7edf7c636ed349df2592ef85329cddValerie Bubb Fenwick i->uid_set ? i->uid : UID_INVALID,
2225707c7e7edf7c636ed349df2592ef85329cddValerie Bubb Fenwick i->gid_set ? i->gid : GID_INVALID);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (chown(path,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM i->uid_set ? i->uid : UID_INVALID,
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM i->gid_set ? i->gid : GID_INVALID) < 0)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return log_error_errno(errno, "chown(%s) failed: %m", path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return label_fix(path, false, false);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM}
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic int get_xattrs_from_arg(Item *i) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM char *xattr;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM const char *p;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM int r;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM assert(i);
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(i->argument);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz p = i->argument;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz _cleanup_free_ char *tmp = NULL, *name = NULL,
df8bdeb362277e8d95a74d6c097341fe97409948johnz *value = NULL, *value2 = NULL, *_xattr = xattr;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = split_pair(xattr, "=", &name, &value);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (strempty(name) || strempty(value)) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr);
df8bdeb362277e8d95a74d6c097341fe97409948johnz continue;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz tmp = unquote(value, "\"");
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!tmp)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return log_oom();
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz value2 = cunescape(tmp);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!value2)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return log_oom();
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (strv_push_pair(&i->xattrs, name, value2) < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return log_oom();
df8bdeb362277e8d95a74d6c097341fe97409948johnz name = value2 = NULL;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int path_set_xattrs(Item *i, const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz char **name, **value;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(i);
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(path);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz STRV_FOREACH_PAIR(name, value, i->xattrs) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz int n;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz n = strlen(*value);
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("\"%s\": setting xattr \"%s=%s\"", path, *name, *value);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (lsetxattr(path, *name, *value, n, 0) < 0) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_error("Setting extended attribute %s=%s on %s failed: %m",
df8bdeb362277e8d95a74d6c097341fe97409948johnz *name, *value, path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz return -errno;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz return 0;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int get_acls_from_arg(Item *item) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz#ifdef HAVE_ACL
df8bdeb362277e8d95a74d6c097341fe97409948johnz int r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(item);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* If force (= modify) is set, we will not modify the acl
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM * afterwards, so the mask can be added now if necessary. */
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM if (r < 0)
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring",
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM item->argument);
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM#else
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM#endif
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM return 0;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM}
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM#ifdef HAVE_ACL
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COMstatic int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM _cleanup_(acl_freep) acl_t dup = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM int r;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM _cleanup_(acl_free_charpp) char *t = NULL;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM /* Returns 0 for success, positive error if already warned,
df8bdeb362277e8d95a74d6c097341fe97409948johnz * negative error otherwise. */
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (modify) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = acls_for_file(path, type, acl, &dup);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = calc_acl_mask_if_needed(&dup);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz } else {
df8bdeb362277e8d95a74d6c097341fe97409948johnz dup = acl_dup(acl);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (!dup)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return -errno;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz /* the mask was already added earlier if needed */
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = add_base_acls_if_needed(&dup, path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug("\"%s\": setting %s ACL \"%s\"", path,
df8bdeb362277e8d95a74d6c097341fe97409948johnz type == ACL_TYPE_ACCESS ? "access" : "default",
df8bdeb362277e8d95a74d6c097341fe97409948johnz strna(t));
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = acl_set_file(path, type, dup);
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return -log_error_errno(errno,
df8bdeb362277e8d95a74d6c097341fe97409948johnz "Setting %s ACL \"%s\" on %s failed: %m",
df8bdeb362277e8d95a74d6c097341fe97409948johnz type == ACL_TYPE_ACCESS ? "access" : "default",
df8bdeb362277e8d95a74d6c097341fe97409948johnz strna(t), path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz return 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz#endif
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int path_set_acls(Item *item, const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz int r = 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz#ifdef HAVE_ACL
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(item);
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (item->acl_access)
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r == 0 && item->acl_default)
df8bdeb362277e8d95a74d6c097341fe97409948johnz r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (r > 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz return -r; /* already warned */
df8bdeb362277e8d95a74d6c097341fe97409948johnz else if (r == -ENOTSUP) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug_errno(r, "ACLs not supported by file system at %s", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz return 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz } else if (r < 0)
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz#endif
df8bdeb362277e8d95a74d6c097341fe97409948johnz return r;
df8bdeb362277e8d95a74d6c097341fe97409948johnz}
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnzstatic int write_one_file(Item *i, const char *path) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz _cleanup_close_ int fd = -1;
df8bdeb362277e8d95a74d6c097341fe97409948johnz int flags, r = 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz struct stat st;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(i);
df8bdeb362277e8d95a74d6c097341fe97409948johnz assert(path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
df8bdeb362277e8d95a74d6c097341fe97409948johnz i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz RUN_WITH_UMASK(0000) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz mac_selinux_create_file_prepare(path, S_IFREG);
df8bdeb362277e8d95a74d6c097341fe97409948johnz fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
df8bdeb362277e8d95a74d6c097341fe97409948johnz mac_selinux_create_file_clear();
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (fd < 0) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (i->type == WRITE_FILE && errno == ENOENT) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_debug_errno(errno, "Not writing \"%s\": %m", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz return 0;
df8bdeb362277e8d95a74d6c097341fe97409948johnz }
df8bdeb362277e8d95a74d6c097341fe97409948johnz
df8bdeb362277e8d95a74d6c097341fe97409948johnz log_error_errno(errno, "Failed to create file %s: %m", path);
df8bdeb362277e8d95a74d6c097341fe97409948johnz return -errno;
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM }
8bab47abcb471dffa36ddbf409a8ef5303398ddfJohn.Zolnowsky@Sun.COM
df8bdeb362277e8d95a74d6c097341fe97409948johnz if (i->argument) {
df8bdeb362277e8d95a74d6c097341fe97409948johnz _cleanup_free_ char *unescaped;
df8bdeb362277e8d95a74d6c097341fe97409948johnz
log_debug("%s to \"%s\".",
i->type == CREATE_FILE ? "Appending" : "Writing", path);
unescaped = cunescape(i->argument);
if (!unescaped)
return log_oom();
r = loop_write(fd, unescaped, strlen(unescaped), false);
if (r < 0)
return log_error_errno(r, "Failed to write file \"%s\": %m", path);
} else
log_debug("\"%s\" has been created.", path);
fd = safe_close(fd);
if (stat(path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", path);
if (!S_ISREG(st.st_mode)) {
log_error("%s is not a file.", path);
return -EEXIST;
}
r = path_set_perms(i, path);
if (r < 0)
return r;
return 0;
}
typedef int (*action_t)(Item *, const char *);
static int item_do_children(Item *i, const char *path, action_t action) {
_cleanup_closedir_ DIR *d;
int r = 0;
assert(i);
assert(path);
/* This returns the first error we run into, but nevertheless
* tries to go on */
d = opendir_nomod(path);
if (!d)
return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
for (;;) {
_cleanup_free_ char *p = NULL;
struct dirent *de;
int q;
errno = 0;
de = readdir(d);
if (!de) {
if (errno != 0 && r == 0)
r = -errno;
break;
}
if (STR_IN_SET(de->d_name, ".", ".."))
continue;
p = strjoin(path, "/", de->d_name, NULL);
if (!p)
return -ENOMEM;
q = action(i, p);
if (q < 0 && q != -ENOENT && r == 0)
r = q;
if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
q = item_do_children(i, p, action);
if (q < 0 && r == 0)
r = q;
}
}
return r;
}
static int glob_item(Item *i, action_t action, bool recursive) {
_cleanup_globfree_ glob_t g = {
.gl_closedir = (void (*)(void *)) closedir,
.gl_readdir = (struct dirent *(*)(void *)) readdir,
.gl_opendir = (void *(*)(const char *)) opendir_nomod,
.gl_lstat = lstat,
.gl_stat = stat,
};
int r = 0, k;
char **fn;
errno = 0;
k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
if (k != 0 && k != GLOB_NOMATCH)
return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
STRV_FOREACH(fn, g.gl_pathv) {
k = action(i, *fn);
if (k < 0 && r == 0)
r = k;
if (recursive) {
k = item_do_children(i, *fn, action);
if (k < 0 && r == 0)
r = k;
}
}
return r;
}
typedef enum {
CREATION_NORMAL,
CREATION_EXISTING,
CREATION_FORCE,
_CREATION_MODE_MAX,
_CREATION_MODE_INVALID = -1
} CreationMode;
static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
[CREATION_NORMAL] = "Created",
[CREATION_EXISTING] = "Found existing",
[CREATION_FORCE] = "Created replacement",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
static int create_item(Item *i) {
struct stat st;
int r = 0;
CreationMode creation;
assert(i);
log_debug("Running create action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
case IGNORE_PATH:
case IGNORE_DIRECTORY_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
return 0;
case CREATE_FILE:
case TRUNCATE_FILE:
r = write_one_file(i, i->path);
if (r < 0)
return r;
break;
case COPY_FILES:
log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
r = copy_tree(i->argument, i->path, false);
if (r < 0) {
struct stat a, b;
if (r != -EEXIST)
return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
if (stat(i->argument, &a) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
if (stat(i->path, &b) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if ((a.st_mode ^ b.st_mode) & S_IFMT) {
log_debug("Can't copy to %s, file exists already and is of different type", i->path);
return 0;
}
}
r = path_set_perms(i, i->path);
if (r < 0)
return r;
break;
case WRITE_FILE:
r = glob_item(i, write_one_file, false);
if (r < 0)
return r;
break;
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_SUBVOLUME:
RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
if (i->type == CREATE_SUBVOLUME)
RUN_WITH_UMASK((~i->mode) & 0777) {
r = btrfs_subvol_make(i->path);
log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
}
else
r = 0;
if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
RUN_WITH_UMASK(0000)
r = mkdir_label(i->path, i->mode);
if (r < 0) {
if (r != -EEXIST)
return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
if (stat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if (!S_ISDIR(st.st_mode)) {
log_debug("\"%s\" already exists and is not a directory.", i->path);
return 0;
}
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
r = path_set_perms(i, i->path);
if (r < 0)
return r;
break;
case CREATE_FIFO:
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
mac_selinux_create_file_clear();
}
if (r < 0) {
if (errno != EEXIST)
return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
if (stat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if (!S_ISFIFO(st.st_mode)) {
if (i->force) {
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo_atomic(i->path, i->mode);
mac_selinux_create_file_clear();
}
if (r < 0)
return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
creation = CREATION_FORCE;
} else {
log_debug("%s is not a fifo.", i->path);
return 0;
}
} else
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
r = path_set_perms(i, i->path);
if (r < 0)
return r;
break;
case CREATE_SYMLINK:
mac_selinux_create_file_prepare(i->path, S_IFLNK);
r = symlink(i->argument, i->path);
mac_selinux_create_file_clear();
if (r < 0) {
_cleanup_free_ char *x = NULL;
if (errno != EEXIST)
return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
r = readlink_malloc(i->path, &x);
if (r < 0 || !streq(i->argument, x)) {
if (i->force) {
mac_selinux_create_file_prepare(i->path, S_IFLNK);
r = symlink_atomic(i->argument, i->path);
mac_selinux_create_file_clear();
if (r < 0)
return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
creation = CREATION_FORCE;
} else {
log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
return 0;
}
} else
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
break;
case CREATE_BLOCK_DEVICE:
case CREATE_CHAR_DEVICE: {
mode_t file_type;
if (have_effective_cap(CAP_MKNOD) == 0) {
/* In a container we lack CAP_MKNOD. We
shouldn't attempt to create the device node in
that case to avoid noise, and we don't support
virtualized devices in containers anyway. */
log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
return 0;
}
file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, file_type);
r = mknod(i->path, i->mode | file_type, i->major_minor);
mac_selinux_create_file_clear();
}
if (r < 0) {
if (errno == EPERM) {
log_debug("We lack permissions, possibly because of cgroup configuration; "
"skipping creation of device node %s.", i->path);
return 0;
}
if (errno != EEXIST)
return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
if (stat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if ((st.st_mode & S_IFMT) != file_type) {
if (i->force) {
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, file_type);
r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
mac_selinux_create_file_clear();
}
if (r < 0)
return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
creation = CREATION_FORCE;
} else {
log_debug("%s is not a device node.", i->path);
return 0;
}
} else
creation = CREATION_EXISTING;
} else
creation = CREATION_NORMAL;
log_debug("%s %s device node \"%s\" %u:%u.",
creation_mode_verb_to_string(creation),
i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
i->path, major(i->mode), minor(i->mode));
r = path_set_perms(i, i->path);
if (r < 0)
return r;
break;
}
case ADJUST_MODE:
case RELABEL_PATH:
r = glob_item(i, path_set_perms, false);
if (r < 0)
return r;
break;
case RECURSIVE_RELABEL_PATH:
r = glob_item(i, path_set_perms, true);
if (r < 0)
return r;
break;
case SET_XATTR:
r = glob_item(i, path_set_xattrs, false);
if (r < 0)
return r;
break;
case RECURSIVE_SET_XATTR:
r = glob_item(i, path_set_xattrs, true);
if (r < 0)
return r;
break;
case SET_ACL:
r = glob_item(i, path_set_acls, false);
if (r < 0)
return r;
break;
case RECURSIVE_SET_ACL:
r = glob_item(i, path_set_acls, true);
if (r < 0)
return r;
break;
}
log_debug("%s created successfully.", i->path);
return 0;
}
static int remove_item_instance(Item *i, const char *instance) {
int r;
assert(i);
switch (i->type) {
case REMOVE_PATH:
if (remove(instance) < 0 && errno != ENOENT)
return log_error_errno(errno, "rm(%s): %m", instance);
break;
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
/* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf() so that 'x' is honoured. */
log_debug("rm -rf \"%s\"", instance);
r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "rm_rf(%s): %m", instance);
break;
default:
assert_not_reached("wut?");
}
return 0;
}
static int remove_item(Item *i) {
int r = 0;
assert(i);
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
case CREATE_FIFO:
case CREATE_SYMLINK:
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE:
case IGNORE_PATH:
case IGNORE_DIRECTORY_PATH:
case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
case COPY_FILES:
case SET_XATTR:
case RECURSIVE_SET_XATTR:
case SET_ACL:
case RECURSIVE_SET_ACL:
break;
case REMOVE_PATH:
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
r = glob_item(i, remove_item_instance, false);
break;
}
return r;
}
static int clean_item_instance(Item *i, const char* instance) {
_cleanup_closedir_ DIR *d = NULL;
struct stat s, ps;
bool mountpoint;
usec_t cutoff, n;
char timestamp[FORMAT_TIMESTAMP_MAX];
assert(i);
if (!i->age_set)
return 0;
n = now(CLOCK_REALTIME);
if (n < i->age)
return 0;
cutoff = n - i->age;
d = opendir_nomod(instance);
if (!d) {
if (errno == ENOENT || errno == ENOTDIR) {
log_debug_errno(errno, "Directory \"%s\": %m", instance);
return 0;
}
log_error_errno(errno, "Failed to open directory %s: %m", instance);
return -errno;
}
if (fstat(dirfd(d), &s) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if (!S_ISDIR(s.st_mode)) {
log_error("%s is not a directory.", i->path);
return -ENOTDIR;
}
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
mountpoint = s.st_dev != ps.st_dev ||
(s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
log_debug("Cleanup threshold for %s \"%s\" is %s",
mountpoint ? "mount point" : "directory",
instance,
format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
MAX_DEPTH, i->keep_first_level);
}
static int clean_item(Item *i) {
int r = 0;
assert(i);
log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
clean_item_instance(i, i->path);
break;
case IGNORE_DIRECTORY_PATH:
r = glob_item(i, clean_item_instance, false);
break;
default:
break;
}
return r;
}
static int process_item_array(ItemArray *array);
static int process_item(Item *i) {
int r, q, p, t = 0;
_cleanup_free_ char *prefix = NULL;
assert(i);
if (i->done)
return 0;
i->done = true;
prefix = malloc(strlen(i->path) + 1);
if (!prefix)
return log_oom();
PATH_FOREACH_PREFIX(prefix, i->path) {
ItemArray *j;
j = hashmap_get(items, prefix);
if (j) {
int s;
s = process_item_array(j);
if (s < 0 && t == 0)
t = s;
}
}
r = arg_create ? create_item(i) : 0;
q = arg_remove ? remove_item(i) : 0;
p = arg_clean ? clean_item(i) : 0;
return t < 0 ? t :
r < 0 ? r :
q < 0 ? q :
p;
}
static int process_item_array(ItemArray *array) {
unsigned n;
int r = 0, k;
assert(array);
for (n = 0; n < array->count; n++) {
k = process_item(array->items + n);
if (k < 0 && r == 0)
r = k;
}
return r;
}
static void item_free_contents(Item *i) {
assert(i);
free(i->path);
free(i->argument);
strv_free(i->xattrs);
#ifdef HAVE_ACL
acl_free(i->acl_access);
acl_free(i->acl_default);
#endif
}
static void item_array_free(ItemArray *a) {
unsigned n;
if (!a)
return;
for (n = 0; n < a->count; n++)
item_free_contents(a->items + n);
free(a->items);
free(a);
}
static bool item_compatible(Item *a, Item *b) {
assert(a);
assert(b);
assert(streq(a->path, b->path));
if (takes_ownership(a->type) && takes_ownership(b->type))
/* check if the items are the same */
return streq_ptr(a->argument, b->argument) &&
a->uid_set == b->uid_set &&
a->uid == b->uid &&
a->gid_set == b->gid_set &&
a->gid == b->gid &&
a->mode_set == b->mode_set &&
a->mode == b->mode &&
a->age_set == b->age_set &&
a->age == b->age &&
a->mask_perms == b->mask_perms &&
a->keep_first_level == b->keep_first_level &&
a->major_minor == b->major_minor;
return true;
}
static bool should_include_path(const char *path) {
char **prefix;
STRV_FOREACH(prefix, arg_exclude_prefixes)
if (path_startswith(path, *prefix)) {
log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
path, *prefix);
return false;
}
STRV_FOREACH(prefix, arg_include_prefixes)
if (path_startswith(path, *prefix)) {
log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
return true;
}
/* no matches, so we should include this path only if we
* have no whitelist at all */
if (strv_length(arg_include_prefixes) == 0)
return true;
log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
return false;
}
static int parse_line(const char *fname, unsigned line, const char *buffer) {
static const Specifier specifier_table[] = {
{ 'm', specifier_machine_id, NULL },
{ 'b', specifier_boot_id, NULL },
{ 'H', specifier_host_name, NULL },
{ 'v', specifier_kernel_release, NULL },
{}
};
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
_cleanup_(item_free_contents) Item i = {};
ItemArray *existing;
Hashmap *h;
int r, pos;
bool force = false, boot = false;
assert(fname);
assert(line >= 1);
assert(buffer);
r = unquote_many_words(&buffer,
&action,
&path,
&mode,
&user,
&group,
&age,
&i.argument,
NULL);
if (r < 0)
return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
else if (r < 2) {
log_error("[%s:%u] Syntax error.", fname, line);
return -EIO;
}
if (isempty(action)) {
log_error("[%s:%u] Command too short '%s'.", fname, line, action);
return -EINVAL;
}
for (pos = 1; action[pos]; pos++) {
if (action[pos] == '!' && !boot)
boot = true;
else if (action[pos] == '+' && !force)
force = true;
else {
log_error("[%s:%u] Unknown modifiers in command '%s'",
fname, line, action);
return -EINVAL;
}
}
if (boot && !arg_boot) {
log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
action, path);
return 0;
}
i.type = action[0];
i.force = force;
r = specifier_printf(path, specifier_table, NULL, &i.path);
if (r < 0) {
log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
return r;
}
switch (i.type) {
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
case IGNORE_DIRECTORY_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
break;
case CREATE_SYMLINK:
if (!i.argument) {
i.argument = strappend("/usr/share/factory/", i.path);
if (!i.argument)
return log_oom();
}
break;
case WRITE_FILE:
if (!i.argument) {
log_error("[%s:%u] Write file requires argument.", fname, line);
return -EBADMSG;
}
break;
case COPY_FILES:
if (!i.argument) {
i.argument = strappend("/usr/share/factory/", i.path);
if (!i.argument)
return log_oom();
} else if (!path_is_absolute(i.argument)) {
log_error("[%s:%u] Source path is not absolute.", fname, line);
return -EBADMSG;
}
path_kill_slashes(i.argument);
break;
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE: {
unsigned major, minor;
if (!i.argument) {
log_error("[%s:%u] Device file requires argument.", fname, line);
return -EBADMSG;
}
if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
return -EBADMSG;
}
i.major_minor = makedev(major, minor);
break;
}
case SET_XATTR:
case RECURSIVE_SET_XATTR:
if (!i.argument) {
log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
return -EBADMSG;
}
r = get_xattrs_from_arg(&i);
if (r < 0)
return r;
break;
case SET_ACL:
case RECURSIVE_SET_ACL:
if (!i.argument) {
log_error("[%s:%u] Set ACLs requires argument.", fname, line);
return -EBADMSG;
}
r = get_acls_from_arg(&i);
if (r < 0)
return r;
break;
default:
log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
return -EBADMSG;
}
if (!path_is_absolute(i.path)) {
log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
return -EBADMSG;
}
path_kill_slashes(i.path);
if (!should_include_path(i.path))
return 0;
if (arg_root) {
char *p;
p = strappend(arg_root, i.path);
if (!p)
return log_oom();
free(i.path);
i.path = p;
}
if (user && !streq(user, "-")) {
const char *u = user;
r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
if (r < 0) {
log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
return r;
}
i.uid_set = true;
}
if (group && !streq(group, "-")) {
const char *g = group;
r = get_group_creds(&g, &i.gid);
if (r < 0) {
log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
return r;
}
i.gid_set = true;
}
if (mode && !streq(mode, "-")) {
const char *mm = mode;
unsigned m;
if (*mm == '~') {
i.mask_perms = true;
mm++;
}
if (sscanf(mm, "%o", &m) != 1) {
log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
return -ENOENT;
}
i.mode = m;
i.mode_set = true;
} else
i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
? 0755 : 0644;
if (age && !streq(age, "-")) {
const char *a = age;
if (*a == '~') {
i.keep_first_level = true;
a++;
}
if (parse_sec(a, &i.age) < 0) {
log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
return -EBADMSG;
}
i.age_set = true;
}
h = needs_glob(i.type) ? globs : items;
existing = hashmap_get(h, i.path);
if (existing) {
unsigned n;
for (n = 0; n < existing->count; n++) {
if (!item_compatible(existing->items + n, &i)) {
log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
fname, line, i.path);
return 0;
}
}
} else {
existing = new0(ItemArray, 1);
r = hashmap_put(h, i.path, existing);
if (r < 0)
return log_oom();
}
if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
return log_oom();
memcpy(existing->items + existing->count++, &i, sizeof(i));
zero(i);
return 0;
}
static void help(void) {
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
"Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --create Create marked files/directories\n"
" --clean Clean up marked directories\n"
" --remove Remove marked files/directories\n"
" --boot Execute actions only safe at boot\n"
" --prefix=PATH Only apply rules with the specified prefix\n"
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
" --root=PATH Operate on an alternate filesystem root\n",
program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_CREATE,
ARG_CLEAN,
ARG_REMOVE,
ARG_BOOT,
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "create", no_argument, NULL, ARG_CREATE },
{ "clean", no_argument, NULL, ARG_CLEAN },
{ "remove", no_argument, NULL, ARG_REMOVE },
{ "boot", no_argument, NULL, ARG_BOOT },
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
{ "root", required_argument, NULL, ARG_ROOT },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_CREATE:
arg_create = true;
break;
case ARG_CLEAN:
arg_clean = true;
break;
case ARG_REMOVE:
arg_remove = true;
break;
case ARG_BOOT:
arg_boot = true;
break;
case ARG_PREFIX:
if (strv_push(&arg_include_prefixes, optarg) < 0)
return log_oom();
break;
case ARG_EXCLUDE_PREFIX:
if (strv_push(&arg_exclude_prefixes, optarg) < 0)
return log_oom();
break;
case ARG_ROOT:
free(arg_root);
arg_root = path_make_absolute_cwd(optarg);
if (!arg_root)
return log_oom();
path_kill_slashes(arg_root);
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
if (!arg_clean && !arg_create && !arg_remove) {
log_error("You need to specify at least one of --clean, --create or --remove.");
return -EINVAL;
}
return 1;
}
static int read_config_file(const char *fn, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
Iterator iterator;
unsigned v = 0;
Item *i;
int r;
assert(fn);
r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
if (r < 0) {
if (ignore_enoent && r == -ENOENT) {
log_debug_errno(r, "Failed to open \"%s\": %m", fn);
return 0;
}
return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
}
log_debug("Reading config file \"%s\".", fn);
FOREACH_LINE(line, f, break) {
char *l;
int k;
v++;
l = strstrip(line);
if (*l == '#' || *l == 0)
continue;
k = parse_line(fn, v, l);
if (k < 0 && r == 0)
r = k;
}
/* we have to determine age parameter for each entry of type X */
HASHMAP_FOREACH(i, globs, iterator) {
Iterator iter;
Item *j, *candidate_item = NULL;
if (i->type != IGNORE_DIRECTORY_PATH)
continue;
HASHMAP_FOREACH(j, items, iter) {
if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
continue;
if (path_equal(j->path, i->path)) {
candidate_item = j;
break;
}
if ((!candidate_item && path_startswith(i->path, j->path)) ||
(candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
candidate_item = j;
}
if (candidate_item && candidate_item->age_set) {
i->age = candidate_item->age;
i->age_set = true;
}
}
if (ferror(f)) {
log_error_errno(errno, "Failed to read from file %s: %m", fn);
if (r == 0)
r = -EIO;
}
return r;
}
int main(int argc, char *argv[]) {
int r, k;
ItemArray *a;
Iterator iterator;
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
mac_selinux_init(NULL);
items = hashmap_new(&string_hash_ops);
globs = hashmap_new(&string_hash_ops);
if (!items || !globs) {
r = log_oom();
goto finish;
}
r = 0;
if (optind < argc) {
int j;
for (j = optind; j < argc; j++) {
k = read_config_file(argv[j], false);
if (k < 0 && r == 0)
r = k;
}
} else {
_cleanup_strv_free_ char **files = NULL;
char **f;
r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
goto finish;
}
STRV_FOREACH(f, files) {
k = read_config_file(*f, true);
if (k < 0 && r == 0)
r = k;
}
}
HASHMAP_FOREACH(a, globs, iterator) {
k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
HASHMAP_FOREACH(a, items, iterator) {
k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
finish:
while ((a = hashmap_steal_first(items)))
item_array_free(a);
while ((a = hashmap_steal_first(globs)))
item_array_free(a);
hashmap_free(items);
hashmap_free(globs);
free(arg_include_prefixes);
free(arg_exclude_prefixes);
free(arg_root);
set_free_free(unix_sockets);
mac_selinux_finish();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}