1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering, Kay Sievers
6 Copyright 2015 Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
35 #include <sys/xattr.h>
43 #include "alloc-util.h"
44 #include "btrfs-util.h"
45 #include "capability-util.h"
46 #include "chattr-util.h"
47 #include "conf-files.h"
50 #include "dirent-util.h"
54 #include "format-util.h"
56 #include "glob-util.h"
63 #include "mount-util.h"
64 #include "parse-util.h"
65 #include "path-lookup.h"
66 #include "path-util.h"
68 #include "selinux-util.h"
70 #include "specifier.h"
71 #include "stat-util.h"
72 #include "stdio-util.h"
73 #include "string-table.h"
74 #include "string-util.h"
76 #include "umask-util.h"
77 #include "user-util.h"
80 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
81 * them in the file system. This is intended to be used to create
82 * properly owned directories beneath /tmp, /var/tmp, /run, which are
83 * volatile and hence need to be recreated on bootup. */
85 typedef enum ItemType
{
86 /* These ones take file names */
89 CREATE_DIRECTORY
= 'd',
90 TRUNCATE_DIRECTORY
= 'D',
91 CREATE_SUBVOLUME
= 'v',
92 CREATE_SUBVOLUME_INHERIT_QUOTA
= 'q',
93 CREATE_SUBVOLUME_NEW_QUOTA
= 'Q',
96 CREATE_CHAR_DEVICE
= 'c',
97 CREATE_BLOCK_DEVICE
= 'b',
100 /* These ones take globs */
102 EMPTY_DIRECTORY
= 'e',
104 RECURSIVE_SET_XATTR
= 'T',
106 RECURSIVE_SET_ACL
= 'A',
108 RECURSIVE_SET_ATTRIBUTE
= 'H',
110 IGNORE_DIRECTORY_PATH
= 'X',
112 RECURSIVE_REMOVE_PATH
= 'R',
114 RECURSIVE_RELABEL_PATH
= 'Z',
115 ADJUST_MODE
= 'm', /* legacy, 'z' is identical to this */
118 typedef struct Item
{
134 unsigned attribute_value
;
135 unsigned attribute_mask
;
142 bool attribute_set
:1;
144 bool keep_first_level
:1;
151 typedef struct ItemArray
{
157 typedef enum DirectoryType
{
158 DIRECTORY_RUNTIME
= 0,
165 static bool arg_user
= false;
166 static bool arg_create
= false;
167 static bool arg_clean
= false;
168 static bool arg_remove
= false;
169 static bool arg_boot
= false;
171 static char **arg_include_prefixes
= NULL
;
172 static char **arg_exclude_prefixes
= NULL
;
173 static char *arg_root
= NULL
;
175 #define MAX_DEPTH 256
177 static OrderedHashmap
*items
= NULL
, *globs
= NULL
;
178 static Set
*unix_sockets
= NULL
;
180 static int specifier_machine_id_safe(char specifier
, void *data
, void *userdata
, char **ret
);
181 static int specifier_directory(char specifier
, void *data
, void *userdata
, char **ret
);
183 static const Specifier specifier_table
[] = {
184 { 'm', specifier_machine_id_safe
, NULL
},
185 { 'b', specifier_boot_id
, NULL
},
186 { 'H', specifier_host_name
, NULL
},
187 { 'v', specifier_kernel_release
, NULL
},
189 { 'U', specifier_user_id
, NULL
},
190 { 'u', specifier_user_name
, NULL
},
191 { 'h', specifier_user_home
, NULL
},
192 { 't', specifier_directory
, UINT_TO_PTR(DIRECTORY_RUNTIME
) },
193 { 'S', specifier_directory
, UINT_TO_PTR(DIRECTORY_STATE
) },
194 { 'C', specifier_directory
, UINT_TO_PTR(DIRECTORY_CACHE
) },
195 { 'L', specifier_directory
, UINT_TO_PTR(DIRECTORY_LOGS
) },
199 static int specifier_machine_id_safe(char specifier
, void *data
, void *userdata
, char **ret
) {
202 /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
203 * return a recognizable error so that the caller can skip the rule
206 r
= specifier_machine_id(specifier
, data
, userdata
, ret
);
207 if (IN_SET(r
, -ENOENT
, -ENOMEDIUM
))
213 static int specifier_directory(char specifier
, void *data
, void *userdata
, char **ret
) {
219 static const struct table_entry paths_system
[] = {
220 [DIRECTORY_RUNTIME
] = { SD_PATH_SYSTEM_RUNTIME
},
221 [DIRECTORY_STATE
] = { SD_PATH_SYSTEM_STATE_PRIVATE
},
222 [DIRECTORY_CACHE
] = { SD_PATH_SYSTEM_STATE_CACHE
},
223 [DIRECTORY_LOGS
] = { SD_PATH_SYSTEM_STATE_LOGS
},
226 static const struct table_entry paths_user
[] = {
227 [DIRECTORY_RUNTIME
] = { SD_PATH_USER_RUNTIME
},
228 [DIRECTORY_STATE
] = { SD_PATH_USER_CONFIGURATION
},
229 [DIRECTORY_CACHE
] = { SD_PATH_USER_STATE_CACHE
},
230 [DIRECTORY_LOGS
] = { SD_PATH_USER_CONFIGURATION
, "log" },
234 const struct table_entry
*paths
;
236 assert_cc(ELEMENTSOF(paths_system
) == ELEMENTSOF(paths_user
));
237 paths
= arg_user
? paths_user
: paths_system
;
239 i
= PTR_TO_UINT(data
);
240 assert(i
< ELEMENTSOF(paths_system
));
242 return sd_path_home(paths
[i
].type
, paths
[i
].suffix
, ret
);
245 static int log_unresolvable_specifier(const char *filename
, unsigned line
) {
246 static bool notified
= false;
248 /* In system mode, this is called when /etc is not fully initialized (e.g.
249 * in a chroot environment) where some specifiers are unresolvable. In user
250 * mode, this is called when some variables are not defined. These cases are
251 * not considered as an error so log at LOG_NOTICE only for the first time
252 * and then downgrade this to LOG_DEBUG for the rest. */
254 log_full(notified
? LOG_DEBUG
: LOG_NOTICE
,
255 "[%s:%u] Failed to resolve specifier: %s, skipping",
257 arg_user
? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
260 log_notice("All rules containing unresolvable specifiers will be skipped.");
266 static int user_config_paths(char*** ret
) {
267 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
268 _cleanup_free_
char *persistent_config
= NULL
, *runtime_config
= NULL
, *data_home
= NULL
;
269 _cleanup_strv_free_
char **res
= NULL
;
272 r
= xdg_user_dirs(&config_dirs
, &data_dirs
);
276 r
= xdg_user_config_dir(&persistent_config
, "/user-tmpfiles.d");
277 if (r
< 0 && r
!= -ENXIO
)
280 r
= xdg_user_runtime_dir(&runtime_config
, "/user-tmpfiles.d");
281 if (r
< 0 && r
!= -ENXIO
)
284 r
= xdg_user_data_dir(&data_home
, "/user-tmpfiles.d");
285 if (r
< 0 && r
!= -ENXIO
)
288 r
= strv_extend_strv_concat(&res
, config_dirs
, "/user-tmpfiles.d");
292 r
= strv_extend(&res
, persistent_config
);
296 r
= strv_extend(&res
, runtime_config
);
300 r
= strv_extend(&res
, data_home
);
304 r
= strv_extend_strv_concat(&res
, data_dirs
, "/user-tmpfiles.d");
308 r
= path_strv_make_absolute_cwd(res
);
317 static bool needs_glob(ItemType t
) {
321 IGNORE_DIRECTORY_PATH
,
323 RECURSIVE_REMOVE_PATH
,
327 RECURSIVE_RELABEL_PATH
,
333 RECURSIVE_SET_ATTRIBUTE
);
336 static bool takes_ownership(ItemType t
) {
344 CREATE_SUBVOLUME_INHERIT_QUOTA
,
345 CREATE_SUBVOLUME_NEW_QUOTA
,
353 IGNORE_DIRECTORY_PATH
,
355 RECURSIVE_REMOVE_PATH
);
358 static struct Item
* find_glob(OrderedHashmap
*h
, const char *match
) {
362 ORDERED_HASHMAP_FOREACH(j
, h
, i
) {
365 for (n
= 0; n
< j
->count
; n
++) {
366 Item
*item
= j
->items
+ n
;
368 if (fnmatch(item
->path
, match
, FNM_PATHNAME
|FNM_PERIOD
) == 0)
376 static void load_unix_sockets(void) {
377 _cleanup_fclose_
FILE *f
= NULL
;
383 /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
385 unix_sockets
= set_new(&string_hash_ops
);
389 f
= fopen("/proc/net/unix", "re");
391 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
, errno
,
392 "Failed to open /proc/net/unix, ignoring: %m");
397 r
= read_line(f
, LONG_LINE_MAX
, NULL
);
399 log_warning_errno(r
, "Failed to skip /proc/net/unix header line: %m");
403 log_warning("Premature end of file reading /proc/net/unix.");
408 _cleanup_free_
char *line
= NULL
;
411 r
= read_line(f
, LONG_LINE_MAX
, &line
);
413 log_warning_errno(r
, "Failed to read /proc/net/unix line, ignoring: %m");
416 if (r
== 0) /* EOF */
419 p
= strchr(line
, ':');
427 p
+= strspn(p
, WHITESPACE
);
428 p
+= strcspn(p
, WHITESPACE
); /* skip one more word */
429 p
+= strspn(p
, WHITESPACE
);
440 path_kill_slashes(s
);
442 r
= set_consume(unix_sockets
, s
);
443 if (r
< 0 && r
!= -EEXIST
) {
444 log_warning_errno(r
, "Failed to add AF_UNIX socket to set, ignoring: %m");
452 unix_sockets
= set_free_free(unix_sockets
);
455 static bool unix_socket_alive(const char *fn
) {
461 return !!set_get(unix_sockets
, (char*) fn
);
463 /* We don't know, so assume yes */
467 static int dir_is_mount_point(DIR *d
, const char *subdir
) {
469 int mount_id_parent
, mount_id
;
472 r_p
= name_to_handle_at_loop(dirfd(d
), ".", NULL
, &mount_id_parent
, 0);
476 r
= name_to_handle_at_loop(dirfd(d
), subdir
, NULL
, &mount_id
, 0);
480 /* got no handle; make no assumptions, return error */
481 if (r_p
< 0 && r
< 0)
484 /* got both handles; if they differ, it is a mount point */
485 if (r_p
>= 0 && r
>= 0)
486 return mount_id_parent
!= mount_id
;
488 /* got only one handle; assume different mount points if one
489 * of both queries was not supported by the filesystem */
490 if (IN_SET(r_p
, -ENOSYS
, -EOPNOTSUPP
) || IN_SET(r
, -ENOSYS
, -EOPNOTSUPP
))
499 static DIR* xopendirat_nomod(int dirfd
, const char *path
) {
502 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
|O_NOATIME
);
506 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
510 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
);
512 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
517 static DIR* opendir_nomod(const char *path
) {
518 return xopendirat_nomod(AT_FDCWD
, path
);
521 static int dir_cleanup(
525 const struct stat
*ds
,
530 bool keep_this_level
) {
533 struct timespec times
[2];
534 bool deleted
= false;
537 FOREACH_DIRENT_ALL(dent
, d
, break) {
540 _cleanup_free_
char *sub_path
= NULL
;
542 if (dot_or_dot_dot(dent
->d_name
))
545 if (fstatat(dirfd(d
), dent
->d_name
, &s
, AT_SYMLINK_NOFOLLOW
) < 0) {
549 /* FUSE, NFS mounts, SELinux might return EACCES */
550 r
= log_full_errno(errno
== EACCES
? LOG_DEBUG
: LOG_ERR
, errno
,
551 "stat(%s/%s) failed: %m", p
, dent
->d_name
);
555 /* Stay on the same filesystem */
556 if (s
.st_dev
!= rootdev
) {
557 log_debug("Ignoring \"%s/%s\": different filesystem.", p
, dent
->d_name
);
561 /* Try to detect bind mounts of the same filesystem instance; they
562 * do not differ in device major/minors. This type of query is not
563 * supported on all kernels or filesystem types though. */
564 if (S_ISDIR(s
.st_mode
) && dir_is_mount_point(d
, dent
->d_name
) > 0) {
565 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
570 /* Do not delete read-only files owned by root */
571 if (s
.st_uid
== 0 && !(s
.st_mode
& S_IWUSR
)) {
572 log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p
, dent
->d_name
);
576 sub_path
= strjoin(p
, "/", dent
->d_name
);
582 /* Is there an item configured for this path? */
583 if (ordered_hashmap_get(items
, sub_path
)) {
584 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path
);
588 if (find_glob(globs
, sub_path
)) {
589 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path
);
593 if (S_ISDIR(s
.st_mode
)) {
596 streq(dent
->d_name
, "lost+found") &&
598 log_debug("Ignoring \"%s\".", sub_path
);
603 log_warning("Reached max depth on \"%s\".", sub_path
);
605 _cleanup_closedir_
DIR *sub_dir
;
608 sub_dir
= xopendirat_nomod(dirfd(d
), dent
->d_name
);
611 r
= log_error_errno(errno
, "opendir(%s) failed: %m", sub_path
);
616 q
= dir_cleanup(i
, sub_path
, sub_dir
, &s
, cutoff
, rootdev
, false, maxdepth
-1, false);
621 /* Note: if you are wondering why we don't
622 * support the sticky bit for excluding
623 * directories from cleaning like we do it for
624 * other file system objects: well, the sticky
625 * bit already has a meaning for directories,
626 * so we don't want to overload that. */
628 if (keep_this_level
) {
629 log_debug("Keeping \"%s\".", sub_path
);
633 /* Ignore ctime, we change it when deleting */
634 age
= timespec_load(&s
.st_mtim
);
636 char a
[FORMAT_TIMESTAMP_MAX
];
637 /* Follows spelling in stat(1). */
638 log_debug("Directory \"%s\": modify time %s is too new.",
640 format_timestamp_us(a
, sizeof(a
), age
));
644 age
= timespec_load(&s
.st_atim
);
646 char a
[FORMAT_TIMESTAMP_MAX
];
647 log_debug("Directory \"%s\": access time %s is too new.",
649 format_timestamp_us(a
, sizeof(a
), age
));
653 log_debug("Removing directory \"%s\".", sub_path
);
654 if (unlinkat(dirfd(d
), dent
->d_name
, AT_REMOVEDIR
) < 0)
655 if (!IN_SET(errno
, ENOENT
, ENOTEMPTY
))
656 r
= log_error_errno(errno
, "rmdir(%s): %m", sub_path
);
659 /* Skip files for which the sticky bit is
660 * set. These are semantics we define, and are
661 * unknown elsewhere. See XDG_RUNTIME_DIR
662 * specification for details. */
663 if (s
.st_mode
& S_ISVTX
) {
664 log_debug("Skipping \"%s\": sticky bit set.", sub_path
);
668 if (mountpoint
&& S_ISREG(s
.st_mode
))
669 if (s
.st_uid
== 0 && STR_IN_SET(dent
->d_name
,
673 log_debug("Skipping \"%s\".", sub_path
);
677 /* Ignore sockets that are listed in /proc/net/unix */
678 if (S_ISSOCK(s
.st_mode
) && unix_socket_alive(sub_path
)) {
679 log_debug("Skipping \"%s\": live socket.", sub_path
);
683 /* Ignore device nodes */
684 if (S_ISCHR(s
.st_mode
) || S_ISBLK(s
.st_mode
)) {
685 log_debug("Skipping \"%s\": a device.", sub_path
);
689 /* Keep files on this level around if this is
691 if (keep_this_level
) {
692 log_debug("Keeping \"%s\".", sub_path
);
696 age
= timespec_load(&s
.st_mtim
);
698 char a
[FORMAT_TIMESTAMP_MAX
];
699 /* Follows spelling in stat(1). */
700 log_debug("File \"%s\": modify time %s is too new.",
702 format_timestamp_us(a
, sizeof(a
), age
));
706 age
= timespec_load(&s
.st_atim
);
708 char a
[FORMAT_TIMESTAMP_MAX
];
709 log_debug("File \"%s\": access time %s is too new.",
711 format_timestamp_us(a
, sizeof(a
), age
));
715 age
= timespec_load(&s
.st_ctim
);
717 char a
[FORMAT_TIMESTAMP_MAX
];
718 log_debug("File \"%s\": change time %s is too new.",
720 format_timestamp_us(a
, sizeof(a
), age
));
724 log_debug("unlink \"%s\"", sub_path
);
726 if (unlinkat(dirfd(d
), dent
->d_name
, 0) < 0)
728 r
= log_error_errno(errno
, "unlink(%s): %m", sub_path
);
737 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
];
739 /* Restore original directory timestamps */
740 times
[0] = ds
->st_atim
;
741 times
[1] = ds
->st_mtim
;
743 age1
= timespec_load(&ds
->st_atim
);
744 age2
= timespec_load(&ds
->st_mtim
);
745 log_debug("Restoring access and modification time on \"%s\": %s, %s",
747 format_timestamp_us(a
, sizeof(a
), age1
),
748 format_timestamp_us(b
, sizeof(b
), age2
));
749 if (futimens(dirfd(d
), times
) < 0)
750 log_error_errno(errno
, "utimensat(%s): %m", p
);
756 static int path_set_perms(Item
*i
, const char *path
) {
757 _cleanup_close_
int fd
= -1;
763 /* We open the file with O_PATH here, to make the operation
764 * somewhat atomic. Also there's unfortunately no fchmodat()
765 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
768 fd
= open(path
, O_NOFOLLOW
|O_CLOEXEC
|O_PATH
);
770 int level
= LOG_ERR
, r
= -errno
;
772 /* Option "e" operates only on existing objects. Do not
773 * print errors about non-existent files or directories */
774 if (i
->type
== EMPTY_DIRECTORY
&& errno
== ENOENT
) {
779 log_full_errno(level
, errno
, "Adjusting owner and mode for %s failed: %m", path
);
784 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
785 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
787 if (S_ISLNK(st
.st_mode
))
788 log_debug("Skipping mode and owner fix for symlink %s.", path
);
790 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
791 xsprintf(fn
, "/proc/self/fd/%i", fd
);
793 /* not using i->path directly because it may be a glob */
798 if (!(st
.st_mode
& 0111))
800 if (!(st
.st_mode
& 0222))
802 if (!(st
.st_mode
& 0444))
804 if (!S_ISDIR(st
.st_mode
))
805 m
&= ~07000; /* remove sticky/sgid/suid bit, unless directory */
808 if (m
== (st
.st_mode
& 07777))
809 log_debug("\"%s\" has right mode %o", path
, st
.st_mode
);
811 log_debug("chmod \"%s\" to mode %o", path
, m
);
812 if (chmod(fn
, m
) < 0)
813 return log_error_errno(errno
, "chmod() of %s via %s failed: %m", path
, fn
);
817 if ((i
->uid
!= st
.st_uid
|| i
->gid
!= st
.st_gid
) &&
818 (i
->uid_set
|| i
->gid_set
)) {
819 log_debug("chown \"%s\" to "UID_FMT
"."GID_FMT
,
821 i
->uid_set
? i
->uid
: UID_INVALID
,
822 i
->gid_set
? i
->gid
: GID_INVALID
);
824 i
->uid_set
? i
->uid
: UID_INVALID
,
825 i
->gid_set
? i
->gid
: GID_INVALID
) < 0)
826 return log_error_errno(errno
, "chown() of %s via %s failed: %m", path
, fn
);
832 return label_fix(path
, false, false);
835 static int parse_xattrs_from_arg(Item
*i
) {
845 _cleanup_free_
char *name
= NULL
, *value
= NULL
, *xattr
= NULL
;
847 r
= extract_first_word(&p
, &xattr
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
849 log_warning_errno(r
, "Failed to parse extended attribute '%s', ignoring: %m", p
);
853 r
= split_pair(xattr
, "=", &name
, &value
);
855 log_warning_errno(r
, "Failed to parse extended attribute, ignoring: %s", xattr
);
859 if (isempty(name
) || isempty(value
)) {
860 log_warning("Malformed extended attribute found, ignoring: %s", xattr
);
864 if (strv_push_pair(&i
->xattrs
, name
, value
) < 0)
873 static int path_set_xattrs(Item
*i
, const char *path
) {
874 char **name
, **value
;
879 STRV_FOREACH_PAIR(name
, value
, i
->xattrs
) {
883 log_debug("Setting extended attribute '%s=%s' on %s.", *name
, *value
, path
);
884 if (lsetxattr(path
, *name
, *value
, n
, 0) < 0)
885 return log_error_errno(errno
, "Setting extended attribute %s=%s on %s failed: %m",
886 *name
, *value
, path
);
891 static int parse_acls_from_arg(Item
*item
) {
897 /* If force (= modify) is set, we will not modify the acl
898 * afterwards, so the mask can be added now if necessary. */
900 r
= parse_acl(item
->argument
, &item
->acl_access
, &item
->acl_default
, !item
->force
);
902 log_warning_errno(r
, "Failed to parse ACL \"%s\": %m. Ignoring", item
->argument
);
904 log_warning_errno(ENOSYS
, "ACLs are not supported. Ignoring");
911 static int path_set_acl(const char *path
, const char *pretty
, acl_type_t type
, acl_t acl
, bool modify
) {
912 _cleanup_(acl_free_charpp
) char *t
= NULL
;
913 _cleanup_(acl_freep
) acl_t dup
= NULL
;
916 /* Returns 0 for success, positive error if already warned,
917 * negative error otherwise. */
920 r
= acls_for_file(path
, type
, acl
, &dup
);
924 r
= calc_acl_mask_if_needed(&dup
);
932 /* the mask was already added earlier if needed */
935 r
= add_base_acls_if_needed(&dup
, path
);
939 t
= acl_to_any_text(dup
, NULL
, ',', TEXT_ABBREVIATE
);
940 log_debug("Setting %s ACL %s on %s.",
941 type
== ACL_TYPE_ACCESS
? "access" : "default",
944 r
= acl_set_file(path
, type
, dup
);
946 /* Return positive to indicate we already warned */
947 return -log_error_errno(errno
,
948 "Setting %s ACL \"%s\" on %s failed: %m",
949 type
== ACL_TYPE_ACCESS
? "access" : "default",
956 static int path_set_acls(Item
*item
, const char *path
) {
959 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
960 _cleanup_close_
int fd
= -1;
966 fd
= open(path
, O_NOFOLLOW
|O_CLOEXEC
|O_PATH
);
968 return log_error_errno(errno
, "Adjusting ACL of %s failed: %m", path
);
970 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
971 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
973 if (S_ISLNK(st
.st_mode
)) {
974 log_debug("Skipping ACL fix for symlink %s.", path
);
978 xsprintf(fn
, "/proc/self/fd/%i", fd
);
980 if (item
->acl_access
)
981 r
= path_set_acl(fn
, path
, ACL_TYPE_ACCESS
, item
->acl_access
, item
->force
);
983 if (r
== 0 && item
->acl_default
)
984 r
= path_set_acl(fn
, path
, ACL_TYPE_DEFAULT
, item
->acl_default
, item
->force
);
987 return -r
; /* already warned */
988 else if (r
== -EOPNOTSUPP
) {
989 log_debug_errno(r
, "ACLs not supported by file system at %s", path
);
992 log_error_errno(r
, "ACL operation on \"%s\" failed: %m", path
);
997 #define ATTRIBUTES_ALL \
1006 FS_JOURNAL_DATA_FL | \
1013 static int parse_attribute_from_arg(Item
*item
) {
1015 static const struct {
1019 { 'A', FS_NOATIME_FL
}, /* do not update atime */
1020 { 'S', FS_SYNC_FL
}, /* Synchronous updates */
1021 { 'D', FS_DIRSYNC_FL
}, /* dirsync behaviour (directories only) */
1022 { 'a', FS_APPEND_FL
}, /* writes to file may only append */
1023 { 'c', FS_COMPR_FL
}, /* Compress file */
1024 { 'd', FS_NODUMP_FL
}, /* do not dump file */
1025 { 'e', FS_EXTENT_FL
}, /* Extents */
1026 { 'i', FS_IMMUTABLE_FL
}, /* Immutable file */
1027 { 'j', FS_JOURNAL_DATA_FL
}, /* Reserved for ext3 */
1028 { 's', FS_SECRM_FL
}, /* Secure deletion */
1029 { 'u', FS_UNRM_FL
}, /* Undelete */
1030 { 't', FS_NOTAIL_FL
}, /* file tail should not be merged */
1031 { 'T', FS_TOPDIR_FL
}, /* Top of directory hierarchies */
1032 { 'C', FS_NOCOW_FL
}, /* Do not cow file */
1041 unsigned value
= 0, mask
= 0;
1051 } else if (*p
== '-') {
1054 } else if (*p
== '=') {
1060 if (isempty(p
) && mode
!= MODE_SET
) {
1061 log_error("Setting file attribute on '%s' needs an attribute specification.", item
->path
);
1065 for (; p
&& *p
; p
++) {
1068 for (i
= 0; i
< ELEMENTSOF(attributes
); i
++)
1069 if (*p
== attributes
[i
].character
)
1072 if (i
>= ELEMENTSOF(attributes
)) {
1073 log_error("Unknown file attribute '%c' on '%s'.", *p
, item
->path
);
1077 v
= attributes
[i
].value
;
1079 SET_FLAG(value
, v
, IN_SET(mode
, MODE_ADD
, MODE_SET
));
1084 if (mode
== MODE_SET
)
1085 mask
|= ATTRIBUTES_ALL
;
1089 item
->attribute_mask
= mask
;
1090 item
->attribute_value
= value
;
1091 item
->attribute_set
= true;
1096 static int path_set_attribute(Item
*item
, const char *path
) {
1097 _cleanup_close_
int fd
= -1;
1102 if (!item
->attribute_set
|| item
->attribute_mask
== 0)
1105 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_CLOEXEC
|O_NOATIME
|O_NOFOLLOW
);
1108 return log_error_errno(errno
, "Skipping file attributes adjustment on symlink %s.", path
);
1110 return log_error_errno(errno
, "Cannot open '%s': %m", path
);
1113 if (fstat(fd
, &st
) < 0)
1114 return log_error_errno(errno
, "Cannot stat '%s': %m", path
);
1116 /* Issuing the file attribute ioctls on device nodes is not
1117 * safe, as that will be delivered to the drivers, not the
1118 * file system containing the device node. */
1119 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) {
1120 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path
);
1124 f
= item
->attribute_value
& item
->attribute_mask
;
1126 /* Mask away directory-specific flags */
1127 if (!S_ISDIR(st
.st_mode
))
1128 f
&= ~FS_DIRSYNC_FL
;
1130 r
= chattr_fd(fd
, f
, item
->attribute_mask
);
1132 log_full_errno(IN_SET(r
, -ENOTTY
, -EOPNOTSUPP
) ? LOG_DEBUG
: LOG_WARNING
,
1134 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
1135 path
, item
->attribute_value
, item
->attribute_mask
);
1140 static int write_one_file(Item
*i
, const char *path
) {
1141 _cleanup_close_
int fd
= -1;
1148 flags
= i
->type
== CREATE_FILE
? O_CREAT
|O_APPEND
|O_NOFOLLOW
:
1149 i
->type
== TRUNCATE_FILE
? O_CREAT
|O_TRUNC
|O_NOFOLLOW
: 0;
1151 RUN_WITH_UMASK(0000) {
1152 mac_selinux_create_file_prepare(path
, S_IFREG
);
1153 fd
= open(path
, flags
|O_NDELAY
|O_CLOEXEC
|O_WRONLY
|O_NOCTTY
, i
->mode
);
1154 mac_selinux_create_file_clear();
1158 if (i
->type
== WRITE_FILE
&& errno
== ENOENT
) {
1159 log_debug_errno(errno
, "Not writing \"%s\": %m", path
);
1164 if (!i
->argument
&& errno
== EROFS
&& stat(path
, &st
) == 0 &&
1165 (i
->type
== CREATE_FILE
|| st
.st_size
== 0))
1168 return log_error_errno(r
, "Failed to create file %s: %m", path
);
1172 log_debug("%s to \"%s\".", i
->type
== CREATE_FILE
? "Appending" : "Writing", path
);
1174 r
= loop_write(fd
, i
->argument
, strlen(i
->argument
), false);
1176 return log_error_errno(r
, "Failed to write file \"%s\": %m", path
);
1178 log_debug("\"%s\" has been created.", path
);
1180 fd
= safe_close(fd
);
1182 if (stat(path
, &st
) < 0)
1183 return log_error_errno(errno
, "stat(%s) failed: %m", path
);
1186 if (!S_ISREG(st
.st_mode
)) {
1187 log_error("%s is not a file.", path
);
1191 r
= path_set_perms(i
, path
);
1198 typedef int (*action_t
)(Item
*, const char *);
1200 static int item_do_children(Item
*i
, const char *path
, action_t action
) {
1201 _cleanup_closedir_
DIR *d
;
1208 /* This returns the first error we run into, but nevertheless
1211 d
= opendir_nomod(path
);
1213 return IN_SET(errno
, ENOENT
, ENOTDIR
, ELOOP
) ? 0 : -errno
;
1215 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
) {
1216 _cleanup_free_
char *p
= NULL
;
1219 if (dot_or_dot_dot(de
->d_name
))
1222 p
= strjoin(path
, "/", de
->d_name
);
1227 if (q
< 0 && q
!= -ENOENT
&& r
== 0)
1230 if (IN_SET(de
->d_type
, DT_UNKNOWN
, DT_DIR
)) {
1231 q
= item_do_children(i
, p
, action
);
1232 if (q
< 0 && r
== 0)
1240 static int glob_item(Item
*i
, action_t action
, bool recursive
) {
1241 _cleanup_globfree_ glob_t g
= {
1242 .gl_opendir
= (void *(*)(const char *)) opendir_nomod
,
1247 k
= safe_glob(i
->path
, GLOB_NOSORT
|GLOB_BRACE
, &g
);
1248 if (k
< 0 && k
!= -ENOENT
)
1249 return log_error_errno(k
, "glob(%s) failed: %m", i
->path
);
1251 STRV_FOREACH(fn
, g
.gl_pathv
) {
1253 if (k
< 0 && r
== 0)
1257 k
= item_do_children(i
, *fn
, action
);
1258 if (k
< 0 && r
== 0)
1271 _CREATION_MODE_INVALID
= -1
1274 static const char *creation_mode_verb_table
[_CREATION_MODE_MAX
] = {
1275 [CREATION_NORMAL
] = "Created",
1276 [CREATION_EXISTING
] = "Found existing",
1277 [CREATION_FORCE
] = "Created replacement",
1280 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb
, CreationMode
);
1282 static int create_item(Item
*i
) {
1286 CreationMode creation
;
1290 log_debug("Running create action for entry %c %s", (char) i
->type
, i
->path
);
1295 case IGNORE_DIRECTORY_PATH
:
1297 case RECURSIVE_REMOVE_PATH
:
1302 r
= write_one_file(i
, i
->path
);
1308 log_debug("Copying tree \"%s\" to \"%s\".", i
->argument
, i
->path
);
1309 r
= copy_tree(i
->argument
, i
->path
, i
->uid_set
? i
->uid
: UID_INVALID
, i
->gid_set
? i
->gid
: GID_INVALID
, COPY_REFLINK
);
1311 if (r
== -EROFS
&& stat(i
->path
, &st
) == 0)
1318 return log_error_errno(r
, "Failed to copy files to %s: %m", i
->path
);
1320 if (stat(i
->argument
, &a
) < 0)
1321 return log_error_errno(errno
, "stat(%s) failed: %m", i
->argument
);
1323 if (stat(i
->path
, &b
) < 0)
1324 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1326 if ((a
.st_mode
^ b
.st_mode
) & S_IFMT
) {
1327 log_debug("Can't copy to %s, file exists already and is of different type", i
->path
);
1332 r
= path_set_perms(i
, i
->path
);
1339 r
= glob_item(i
, write_one_file
, false);
1345 case CREATE_DIRECTORY
:
1346 case TRUNCATE_DIRECTORY
:
1347 case CREATE_SUBVOLUME
:
1348 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1349 case CREATE_SUBVOLUME_NEW_QUOTA
:
1350 RUN_WITH_UMASK(0000)
1351 mkdir_parents_label(i
->path
, 0755);
1353 if (IN_SET(i
->type
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
)) {
1355 if (btrfs_is_subvol(isempty(arg_root
) ? "/" : arg_root
) <= 0)
1357 /* Don't create a subvolume unless the
1358 * root directory is one, too. We do
1359 * this under the assumption that if
1360 * the root directory is just a plain
1361 * directory (i.e. very light-weight),
1362 * we shouldn't try to split it up
1363 * into subvolumes (i.e. more
1364 * heavy-weight). Thus, chroot()
1365 * environments and suchlike will get
1366 * a full brtfs subvolume set up below
1367 * their tree only if they
1368 * specifically set up a btrfs
1369 * subvolume for the root dir too. */
1373 RUN_WITH_UMASK((~i
->mode
) & 0777)
1374 r
= btrfs_subvol_make(i
->path
);
1379 if (IN_SET(i
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
) || r
== -ENOTTY
)
1380 RUN_WITH_UMASK(0000)
1381 r
= mkdir_label(i
->path
, i
->mode
);
1386 if (!IN_SET(r
, -EEXIST
, -EROFS
))
1387 return log_error_errno(r
, "Failed to create directory or subvolume \"%s\": %m", i
->path
);
1389 k
= is_dir(i
->path
, false);
1390 if (k
== -ENOENT
&& r
== -EROFS
)
1391 return log_error_errno(r
, "%s does not exist and cannot be created as the file system is read-only.", i
->path
);
1393 return log_error_errno(k
, "Failed to check if %s exists: %m", i
->path
);
1395 log_warning("\"%s\" already exists and is not a directory.", i
->path
);
1399 creation
= CREATION_EXISTING
;
1401 creation
= CREATION_NORMAL
;
1403 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1405 if (IN_SET(i
->type
, CREATE_SUBVOLUME_NEW_QUOTA
, CREATE_SUBVOLUME_INHERIT_QUOTA
)) {
1406 r
= btrfs_subvol_auto_qgroup(i
->path
, 0, i
->type
== CREATE_SUBVOLUME_NEW_QUOTA
);
1408 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i
->path
);
1409 else if (r
== -EROFS
)
1410 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i
->path
);
1411 else if (r
== -ENOPROTOOPT
)
1412 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i
->path
);
1414 q
= log_error_errno(r
, "Failed to adjust quota for subvolume \"%s\": %m", i
->path
);
1416 log_debug("Adjusted quota for subvolume \"%s\".", i
->path
);
1418 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i
->path
);
1422 case EMPTY_DIRECTORY
:
1423 r
= path_set_perms(i
, i
->path
);
1432 RUN_WITH_UMASK(0000) {
1433 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1434 r
= mkfifo(i
->path
, i
->mode
);
1435 mac_selinux_create_file_clear();
1439 if (errno
!= EEXIST
)
1440 return log_error_errno(errno
, "Failed to create fifo %s: %m", i
->path
);
1442 if (lstat(i
->path
, &st
) < 0)
1443 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1445 if (!S_ISFIFO(st
.st_mode
)) {
1448 RUN_WITH_UMASK(0000) {
1449 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1450 r
= mkfifo_atomic(i
->path
, i
->mode
);
1451 mac_selinux_create_file_clear();
1455 return log_error_errno(r
, "Failed to create fifo %s: %m", i
->path
);
1456 creation
= CREATION_FORCE
;
1458 log_warning("\"%s\" already exists and is not a fifo.", i
->path
);
1462 creation
= CREATION_EXISTING
;
1464 creation
= CREATION_NORMAL
;
1465 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1467 r
= path_set_perms(i
, i
->path
);
1474 case CREATE_SYMLINK
: {
1475 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1476 r
= symlink(i
->argument
, i
->path
);
1477 mac_selinux_create_file_clear();
1480 _cleanup_free_
char *x
= NULL
;
1482 if (errno
!= EEXIST
)
1483 return log_error_errno(errno
, "symlink(%s, %s) failed: %m", i
->argument
, i
->path
);
1485 r
= readlink_malloc(i
->path
, &x
);
1486 if (r
< 0 || !streq(i
->argument
, x
)) {
1489 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1490 r
= symlink_atomic(i
->argument
, i
->path
);
1491 mac_selinux_create_file_clear();
1493 if (IN_SET(r
, -EEXIST
, -ENOTEMPTY
)) {
1494 r
= rm_rf(i
->path
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
1496 return log_error_errno(r
, "rm -fr %s failed: %m", i
->path
);
1498 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1499 r
= symlink(i
->argument
, i
->path
) < 0 ? -errno
: 0;
1500 mac_selinux_create_file_clear();
1503 return log_error_errno(r
, "symlink(%s, %s) failed: %m", i
->argument
, i
->path
);
1505 creation
= CREATION_FORCE
;
1507 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i
->path
);
1511 creation
= CREATION_EXISTING
;
1514 creation
= CREATION_NORMAL
;
1515 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1519 case CREATE_BLOCK_DEVICE
:
1520 case CREATE_CHAR_DEVICE
: {
1523 if (have_effective_cap(CAP_MKNOD
) == 0) {
1524 /* In a container we lack CAP_MKNOD. We
1525 shouldn't attempt to create the device node in
1526 that case to avoid noise, and we don't support
1527 virtualized devices in containers anyway. */
1529 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i
->path
);
1533 file_type
= i
->type
== CREATE_BLOCK_DEVICE
? S_IFBLK
: S_IFCHR
;
1535 RUN_WITH_UMASK(0000) {
1536 mac_selinux_create_file_prepare(i
->path
, file_type
);
1537 r
= mknod(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1538 mac_selinux_create_file_clear();
1542 if (errno
== EPERM
) {
1543 log_debug("We lack permissions, possibly because of cgroup configuration; "
1544 "skipping creation of device node %s.", i
->path
);
1548 if (errno
!= EEXIST
)
1549 return log_error_errno(errno
, "Failed to create device node %s: %m", i
->path
);
1551 if (lstat(i
->path
, &st
) < 0)
1552 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1554 if ((st
.st_mode
& S_IFMT
) != file_type
) {
1558 RUN_WITH_UMASK(0000) {
1559 mac_selinux_create_file_prepare(i
->path
, file_type
);
1560 r
= mknod_atomic(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1561 mac_selinux_create_file_clear();
1565 return log_error_errno(r
, "Failed to create device node \"%s\": %m", i
->path
);
1566 creation
= CREATION_FORCE
;
1568 log_debug("%s is not a device node.", i
->path
);
1572 creation
= CREATION_EXISTING
;
1574 creation
= CREATION_NORMAL
;
1576 log_debug("%s %s device node \"%s\" %u:%u.",
1577 creation_mode_verb_to_string(creation
),
1578 i
->type
== CREATE_BLOCK_DEVICE
? "block" : "char",
1579 i
->path
, major(i
->mode
), minor(i
->mode
));
1581 r
= path_set_perms(i
, i
->path
);
1590 r
= glob_item(i
, path_set_perms
, false);
1595 case RECURSIVE_RELABEL_PATH
:
1596 r
= glob_item(i
, path_set_perms
, true);
1602 r
= glob_item(i
, path_set_xattrs
, false);
1607 case RECURSIVE_SET_XATTR
:
1608 r
= glob_item(i
, path_set_xattrs
, true);
1614 r
= glob_item(i
, path_set_acls
, false);
1619 case RECURSIVE_SET_ACL
:
1620 r
= glob_item(i
, path_set_acls
, true);
1626 r
= glob_item(i
, path_set_attribute
, false);
1631 case RECURSIVE_SET_ATTRIBUTE
:
1632 r
= glob_item(i
, path_set_attribute
, true);
1641 static int remove_item_instance(Item
*i
, const char *instance
) {
1649 if (remove(instance
) < 0 && errno
!= ENOENT
)
1650 return log_error_errno(errno
, "rm(%s): %m", instance
);
1654 case TRUNCATE_DIRECTORY
:
1655 case RECURSIVE_REMOVE_PATH
:
1656 /* FIXME: we probably should use dir_cleanup() here
1657 * instead of rm_rf() so that 'x' is honoured. */
1658 log_debug("rm -rf \"%s\"", instance
);
1659 r
= rm_rf(instance
, (i
->type
== RECURSIVE_REMOVE_PATH
? REMOVE_ROOT
|REMOVE_SUBVOLUME
: 0) | REMOVE_PHYSICAL
);
1660 if (r
< 0 && r
!= -ENOENT
)
1661 return log_error_errno(r
, "rm_rf(%s): %m", instance
);
1666 assert_not_reached("wut?");
1672 static int remove_item(Item
*i
) {
1675 log_debug("Running remove action for entry %c %s", (char) i
->type
, i
->path
);
1680 case TRUNCATE_DIRECTORY
:
1681 case RECURSIVE_REMOVE_PATH
:
1682 return glob_item(i
, remove_item_instance
, false);
1689 static int clean_item_instance(Item
*i
, const char* instance
) {
1690 _cleanup_closedir_
DIR *d
= NULL
;
1694 char timestamp
[FORMAT_TIMESTAMP_MAX
];
1701 n
= now(CLOCK_REALTIME
);
1705 cutoff
= n
- i
->age
;
1707 d
= opendir_nomod(instance
);
1709 if (IN_SET(errno
, ENOENT
, ENOTDIR
)) {
1710 log_debug_errno(errno
, "Directory \"%s\": %m", instance
);
1714 return log_error_errno(errno
, "Failed to open directory %s: %m", instance
);
1717 if (fstat(dirfd(d
), &s
) < 0)
1718 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1720 if (!S_ISDIR(s
.st_mode
)) {
1721 log_error("%s is not a directory.", i
->path
);
1725 if (fstatat(dirfd(d
), "..", &ps
, AT_SYMLINK_NOFOLLOW
) != 0)
1726 return log_error_errno(errno
, "stat(%s/..) failed: %m", i
->path
);
1728 mountpoint
= s
.st_dev
!= ps
.st_dev
|| s
.st_ino
== ps
.st_ino
;
1730 log_debug("Cleanup threshold for %s \"%s\" is %s",
1731 mountpoint
? "mount point" : "directory",
1733 format_timestamp_us(timestamp
, sizeof(timestamp
), cutoff
));
1735 return dir_cleanup(i
, instance
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
,
1736 MAX_DEPTH
, i
->keep_first_level
);
1739 static int clean_item(Item
*i
) {
1742 log_debug("Running clean action for entry %c %s", (char) i
->type
, i
->path
);
1745 case CREATE_DIRECTORY
:
1746 case CREATE_SUBVOLUME
:
1747 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1748 case CREATE_SUBVOLUME_NEW_QUOTA
:
1749 case TRUNCATE_DIRECTORY
:
1752 clean_item_instance(i
, i
->path
);
1754 case EMPTY_DIRECTORY
:
1755 case IGNORE_DIRECTORY_PATH
:
1756 return glob_item(i
, clean_item_instance
, false);
1762 static int process_item_array(ItemArray
*array
);
1764 static int process_item(Item
*i
) {
1766 _cleanup_free_
char *prefix
= NULL
;
1775 prefix
= malloc(strlen(i
->path
) + 1);
1779 PATH_FOREACH_PREFIX(prefix
, i
->path
) {
1782 j
= ordered_hashmap_get(items
, prefix
);
1786 s
= process_item_array(j
);
1787 if (s
< 0 && t
== 0)
1792 if (chase_symlinks(i
->path
, NULL
, CHASE_NO_AUTOFS
, NULL
) == -EREMOTE
)
1795 r
= arg_create
? create_item(i
) : 0;
1796 q
= arg_remove
? remove_item(i
) : 0;
1797 p
= arg_clean
? clean_item(i
) : 0;
1805 static int process_item_array(ItemArray
*array
) {
1811 for (n
= 0; n
< array
->count
; n
++) {
1812 k
= process_item(array
->items
+ n
);
1813 if (k
< 0 && r
== 0)
1820 static void item_free_contents(Item
*i
) {
1824 strv_free(i
->xattrs
);
1827 acl_free(i
->acl_access
);
1828 acl_free(i
->acl_default
);
1832 static void item_array_free(ItemArray
*a
) {
1838 for (n
= 0; n
< a
->count
; n
++)
1839 item_free_contents(a
->items
+ n
);
1844 static int item_compare(const void *a
, const void *b
) {
1845 const Item
*x
= a
, *y
= b
;
1847 /* Make sure that the ownership taking item is put first, so
1848 * that we first create the node, and then can adjust it */
1850 if (takes_ownership(x
->type
) && !takes_ownership(y
->type
))
1852 if (!takes_ownership(x
->type
) && takes_ownership(y
->type
))
1855 return (int) x
->type
- (int) y
->type
;
1858 static bool item_compatible(Item
*a
, Item
*b
) {
1861 assert(streq(a
->path
, b
->path
));
1863 if (takes_ownership(a
->type
) && takes_ownership(b
->type
))
1864 /* check if the items are the same */
1865 return streq_ptr(a
->argument
, b
->argument
) &&
1867 a
->uid_set
== b
->uid_set
&&
1870 a
->gid_set
== b
->gid_set
&&
1873 a
->mode_set
== b
->mode_set
&&
1874 a
->mode
== b
->mode
&&
1876 a
->age_set
== b
->age_set
&&
1879 a
->mask_perms
== b
->mask_perms
&&
1881 a
->keep_first_level
== b
->keep_first_level
&&
1883 a
->major_minor
== b
->major_minor
;
1888 static bool should_include_path(const char *path
) {
1891 STRV_FOREACH(prefix
, arg_exclude_prefixes
)
1892 if (path_startswith(path
, *prefix
)) {
1893 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1898 STRV_FOREACH(prefix
, arg_include_prefixes
)
1899 if (path_startswith(path
, *prefix
)) {
1900 log_debug("Entry \"%s\" matches include prefix \"%s\".", path
, *prefix
);
1904 /* no matches, so we should include this path only if we
1905 * have no whitelist at all */
1906 if (strv_isempty(arg_include_prefixes
))
1909 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path
);
1913 static int specifier_expansion_from_arg(Item
*i
) {
1914 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
1920 if (i
->argument
== NULL
)
1925 case CREATE_SYMLINK
:
1929 r
= cunescape(i
->argument
, 0, &unescaped
);
1931 return log_error_errno(r
, "Failed to unescape parameter to write: %s", i
->argument
);
1933 r
= specifier_printf(unescaped
, specifier_table
, NULL
, &resolved
);
1937 free_and_replace(i
->argument
, resolved
);
1941 case RECURSIVE_SET_XATTR
:
1944 STRV_FOREACH (xattr
, i
->xattrs
) {
1945 r
= specifier_printf(*xattr
, specifier_table
, NULL
, &resolved
);
1949 free_and_replace(*xattr
, resolved
);
1959 static int parse_line(const char *fname
, unsigned line
, const char *buffer
, bool *invalid_config
) {
1961 _cleanup_free_
char *action
= NULL
, *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
, *path
= NULL
;
1962 _cleanup_(item_free_contents
) Item i
= {};
1963 ItemArray
*existing
;
1966 bool force
= false, boot
= false;
1972 r
= extract_many_words(
1984 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
1985 /* invalid quoting and such or an unknown specifier */
1986 *invalid_config
= true;
1987 return log_error_errno(r
, "[%s:%u] Failed to parse line: %m", fname
, line
);
1991 *invalid_config
= true;
1992 log_error("[%s:%u] Syntax error.", fname
, line
);
1996 if (!isempty(buffer
) && !streq(buffer
, "-")) {
1997 i
.argument
= strdup(buffer
);
2002 if (isempty(action
)) {
2003 *invalid_config
= true;
2004 log_error("[%s:%u] Command too short '%s'.", fname
, line
, action
);
2008 for (pos
= 1; action
[pos
]; pos
++) {
2009 if (action
[pos
] == '!' && !boot
)
2011 else if (action
[pos
] == '+' && !force
)
2014 *invalid_config
= true;
2015 log_error("[%s:%u] Unknown modifiers in command '%s'",
2016 fname
, line
, action
);
2021 if (boot
&& !arg_boot
) {
2022 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
2030 r
= specifier_printf(path
, specifier_table
, NULL
, &i
.path
);
2032 return log_unresolvable_specifier(fname
, line
);
2034 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2035 *invalid_config
= true;
2036 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers: %s", fname
, line
, path
);
2041 case CREATE_DIRECTORY
:
2042 case CREATE_SUBVOLUME
:
2043 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
2044 case CREATE_SUBVOLUME_NEW_QUOTA
:
2045 case EMPTY_DIRECTORY
:
2046 case TRUNCATE_DIRECTORY
:
2049 case IGNORE_DIRECTORY_PATH
:
2051 case RECURSIVE_REMOVE_PATH
:
2054 case RECURSIVE_RELABEL_PATH
:
2056 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname
, line
, i
.type
);
2064 case CREATE_SYMLINK
:
2066 i
.argument
= strappend("/usr/share/factory/", i
.path
);
2074 *invalid_config
= true;
2075 log_error("[%s:%u] Write file requires argument.", fname
, line
);
2082 i
.argument
= strappend("/usr/share/factory/", i
.path
);
2085 } else if (!path_is_absolute(i
.argument
)) {
2086 *invalid_config
= true;
2087 log_error("[%s:%u] Source path is not absolute.", fname
, line
);
2091 path_kill_slashes(i
.argument
);
2094 case CREATE_CHAR_DEVICE
:
2095 case CREATE_BLOCK_DEVICE
: {
2096 unsigned major
, minor
;
2099 *invalid_config
= true;
2100 log_error("[%s:%u] Device file requires argument.", fname
, line
);
2104 if (sscanf(i
.argument
, "%u:%u", &major
, &minor
) != 2) {
2105 *invalid_config
= true;
2106 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
.argument
);
2110 i
.major_minor
= makedev(major
, minor
);
2115 case RECURSIVE_SET_XATTR
:
2117 *invalid_config
= true;
2118 log_error("[%s:%u] Set extended attribute requires argument.", fname
, line
);
2121 r
= parse_xattrs_from_arg(&i
);
2127 case RECURSIVE_SET_ACL
:
2129 *invalid_config
= true;
2130 log_error("[%s:%u] Set ACLs requires argument.", fname
, line
);
2133 r
= parse_acls_from_arg(&i
);
2139 case RECURSIVE_SET_ATTRIBUTE
:
2141 *invalid_config
= true;
2142 log_error("[%s:%u] Set file attribute requires argument.", fname
, line
);
2145 r
= parse_attribute_from_arg(&i
);
2146 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2147 *invalid_config
= true;
2153 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, (char) i
.type
);
2154 *invalid_config
= true;
2158 if (!path_is_absolute(i
.path
)) {
2159 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
.path
);
2160 *invalid_config
= true;
2164 path_kill_slashes(i
.path
);
2166 if (!should_include_path(i
.path
))
2169 r
= specifier_expansion_from_arg(&i
);
2171 return log_unresolvable_specifier(fname
, line
);
2173 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2174 *invalid_config
= true;
2175 return log_error_errno(r
, "[%s:%u] Failed to substitute specifiers in argument: %m",
2182 p
= prefix_root(arg_root
, i
.path
);
2190 if (!isempty(user
) && !streq(user
, "-")) {
2191 const char *u
= user
;
2193 r
= get_user_creds(&u
, &i
.uid
, NULL
, NULL
, NULL
);
2195 *invalid_config
= true;
2196 return log_error_errno(r
, "[%s:%u] Unknown user '%s'.", fname
, line
, user
);
2202 if (!isempty(group
) && !streq(group
, "-")) {
2203 const char *g
= group
;
2205 r
= get_group_creds(&g
, &i
.gid
);
2207 *invalid_config
= true;
2208 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
2215 if (!isempty(mode
) && !streq(mode
, "-")) {
2216 const char *mm
= mode
;
2220 i
.mask_perms
= true;
2224 if (parse_mode(mm
, &m
) < 0) {
2225 *invalid_config
= true;
2226 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
2233 i
.mode
= IN_SET(i
.type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
) ? 0755 : 0644;
2235 if (!isempty(age
) && !streq(age
, "-")) {
2236 const char *a
= age
;
2239 i
.keep_first_level
= true;
2243 if (parse_sec(a
, &i
.age
) < 0) {
2244 *invalid_config
= true;
2245 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
2252 h
= needs_glob(i
.type
) ? globs
: items
;
2254 existing
= ordered_hashmap_get(h
, i
.path
);
2258 for (n
= 0; n
< existing
->count
; n
++) {
2259 if (!item_compatible(existing
->items
+ n
, &i
)) {
2260 log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2261 fname
, line
, i
.path
);
2266 existing
= new0(ItemArray
, 1);
2267 r
= ordered_hashmap_put(h
, i
.path
, existing
);
2272 if (!GREEDY_REALLOC(existing
->items
, existing
->size
, existing
->count
+ 1))
2275 memcpy(existing
->items
+ existing
->count
++, &i
, sizeof(i
));
2277 /* Sort item array, to enforce stable ordering of application */
2278 qsort_safe(existing
->items
, existing
->count
, sizeof(Item
), item_compare
);
2284 static void help(void) {
2285 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2286 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2287 " -h --help Show this help\n"
2288 " --user Execute user configuration\n"
2289 " --version Show package version\n"
2290 " --create Create marked files/directories\n"
2291 " --clean Clean up marked directories\n"
2292 " --remove Remove marked files/directories\n"
2293 " --boot Execute actions only safe at boot\n"
2294 " --prefix=PATH Only apply rules with the specified prefix\n"
2295 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2296 " --root=PATH Operate on an alternate filesystem root\n"
2297 , program_invocation_short_name
);
2300 static int parse_argv(int argc
, char *argv
[]) {
2303 ARG_VERSION
= 0x100,
2314 static const struct option options
[] = {
2315 { "help", no_argument
, NULL
, 'h' },
2316 { "user", no_argument
, NULL
, ARG_USER
},
2317 { "version", no_argument
, NULL
, ARG_VERSION
},
2318 { "create", no_argument
, NULL
, ARG_CREATE
},
2319 { "clean", no_argument
, NULL
, ARG_CLEAN
},
2320 { "remove", no_argument
, NULL
, ARG_REMOVE
},
2321 { "boot", no_argument
, NULL
, ARG_BOOT
},
2322 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
2323 { "exclude-prefix", required_argument
, NULL
, ARG_EXCLUDE_PREFIX
},
2324 { "root", required_argument
, NULL
, ARG_ROOT
},
2333 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
2365 if (strv_push(&arg_include_prefixes
, optarg
) < 0)
2369 case ARG_EXCLUDE_PREFIX
:
2370 if (strv_push(&arg_exclude_prefixes
, optarg
) < 0)
2375 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
2384 assert_not_reached("Unhandled option");
2387 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
2388 log_error("You need to specify at least one of --clean, --create or --remove.");
2395 static int read_config_file(const char **config_dirs
, const char *fn
, bool ignore_enoent
, bool *invalid_config
) {
2396 _cleanup_fclose_
FILE *_f
= NULL
;
2398 char line
[LINE_MAX
];
2406 if (streq(fn
, "-")) {
2407 log_debug("Reading config from stdin.");
2411 r
= search_and_fopen(fn
, "re", arg_root
, config_dirs
, &_f
);
2413 if (ignore_enoent
&& r
== -ENOENT
) {
2414 log_debug_errno(r
, "Failed to open \"%s\", ignoring: %m", fn
);
2418 return log_error_errno(r
, "Failed to open '%s': %m", fn
);
2420 log_debug("Reading config file \"%s\".", fn
);
2424 FOREACH_LINE(line
, f
, break) {
2427 bool invalid_line
= false;
2432 if (IN_SET(*l
, 0, '#'))
2435 k
= parse_line(fn
, v
, l
, &invalid_line
);
2438 /* Allow reporting with a special code if the caller requested this */
2439 *invalid_config
= true;
2441 /* The first error becomes our return value */
2446 /* we have to determine age parameter for each entry of type X */
2447 ORDERED_HASHMAP_FOREACH(i
, globs
, iterator
) {
2449 Item
*j
, *candidate_item
= NULL
;
2451 if (i
->type
!= IGNORE_DIRECTORY_PATH
)
2454 ORDERED_HASHMAP_FOREACH(j
, items
, iter
) {
2455 if (!IN_SET(j
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
))
2458 if (path_equal(j
->path
, i
->path
)) {
2463 if ((!candidate_item
&& path_startswith(i
->path
, j
->path
)) ||
2464 (candidate_item
&& path_startswith(j
->path
, candidate_item
->path
) && (fnmatch(i
->path
, j
->path
, FNM_PATHNAME
| FNM_PERIOD
) == 0)))
2468 if (candidate_item
&& candidate_item
->age_set
) {
2469 i
->age
= candidate_item
->age
;
2475 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
2483 int main(int argc
, char *argv
[]) {
2487 _cleanup_strv_free_
char **config_dirs
= NULL
;
2488 bool invalid_config
= false;
2491 r
= parse_argv(argc
, argv
);
2495 log_set_target(LOG_TARGET_AUTO
);
2496 log_parse_environment();
2503 items
= ordered_hashmap_new(&string_hash_ops
);
2504 globs
= ordered_hashmap_new(&string_hash_ops
);
2506 if (!items
|| !globs
) {
2514 r
= user_config_paths(&config_dirs
);
2516 log_error_errno(r
, "Failed to initialize configuration directory list: %m");
2520 config_dirs
= strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
2528 _cleanup_free_
char *t
= NULL
;
2530 t
= strv_join(config_dirs
, "\n\t");
2532 log_debug("Looking for configuration files in (higher priority first:\n\t%s", t
);
2535 if (optind
< argc
) {
2538 for (j
= optind
; j
< argc
; j
++) {
2539 k
= read_config_file((const char**) config_dirs
, argv
[j
], false, &invalid_config
);
2540 if (k
< 0 && r
== 0)
2545 _cleanup_strv_free_
char **files
= NULL
;
2547 r
= conf_files_list_strv(&files
, ".conf", arg_root
, 0, (const char* const*) config_dirs
);
2549 log_error_errno(r
, "Failed to enumerate tmpfiles.d files: %m");
2553 STRV_FOREACH(f
, files
) {
2554 k
= read_config_file((const char**) config_dirs
, *f
, true, &invalid_config
);
2555 if (k
< 0 && r
== 0)
2560 /* The non-globbing ones usually create things, hence we apply
2562 ORDERED_HASHMAP_FOREACH(a
, items
, iterator
) {
2563 k
= process_item_array(a
);
2564 if (k
< 0 && r
== 0)
2568 /* The globbing ones usually alter things, hence we apply them
2570 ORDERED_HASHMAP_FOREACH(a
, globs
, iterator
) {
2571 k
= process_item_array(a
);
2572 if (k
< 0 && r
== 0)
2577 ordered_hashmap_free_with_destructor(items
, item_array_free
);
2578 ordered_hashmap_free_with_destructor(globs
, item_array_free
);
2580 free(arg_include_prefixes
);
2581 free(arg_exclude_prefixes
);
2584 set_free_free(unix_sockets
);
2586 mac_selinux_finish();
2589 return EXIT_FAILURE
;
2590 else if (invalid_config
)
2593 return EXIT_SUCCESS
;