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
;
174 static char *arg_replace
= NULL
;
176 #define MAX_DEPTH 256
178 static OrderedHashmap
*items
= NULL
, *globs
= NULL
;
179 static Set
*unix_sockets
= NULL
;
181 static int specifier_machine_id_safe(char specifier
, void *data
, void *userdata
, char **ret
);
182 static int specifier_directory(char specifier
, void *data
, void *userdata
, char **ret
);
184 static const Specifier specifier_table
[] = {
185 { 'm', specifier_machine_id_safe
, NULL
},
186 { 'b', specifier_boot_id
, NULL
},
187 { 'H', specifier_host_name
, NULL
},
188 { 'v', specifier_kernel_release
, NULL
},
190 { 'U', specifier_user_id
, NULL
},
191 { 'u', specifier_user_name
, NULL
},
192 { 'h', specifier_user_home
, NULL
},
193 { 't', specifier_directory
, UINT_TO_PTR(DIRECTORY_RUNTIME
) },
194 { 'S', specifier_directory
, UINT_TO_PTR(DIRECTORY_STATE
) },
195 { 'C', specifier_directory
, UINT_TO_PTR(DIRECTORY_CACHE
) },
196 { 'L', specifier_directory
, UINT_TO_PTR(DIRECTORY_LOGS
) },
200 static int specifier_machine_id_safe(char specifier
, void *data
, void *userdata
, char **ret
) {
203 /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
204 * return a recognizable error so that the caller can skip the rule
207 r
= specifier_machine_id(specifier
, data
, userdata
, ret
);
208 if (IN_SET(r
, -ENOENT
, -ENOMEDIUM
))
214 static int specifier_directory(char specifier
, void *data
, void *userdata
, char **ret
) {
220 static const struct table_entry paths_system
[] = {
221 [DIRECTORY_RUNTIME
] = { SD_PATH_SYSTEM_RUNTIME
},
222 [DIRECTORY_STATE
] = { SD_PATH_SYSTEM_STATE_PRIVATE
},
223 [DIRECTORY_CACHE
] = { SD_PATH_SYSTEM_STATE_CACHE
},
224 [DIRECTORY_LOGS
] = { SD_PATH_SYSTEM_STATE_LOGS
},
227 static const struct table_entry paths_user
[] = {
228 [DIRECTORY_RUNTIME
] = { SD_PATH_USER_RUNTIME
},
229 [DIRECTORY_STATE
] = { SD_PATH_USER_CONFIGURATION
},
230 [DIRECTORY_CACHE
] = { SD_PATH_USER_STATE_CACHE
},
231 [DIRECTORY_LOGS
] = { SD_PATH_USER_CONFIGURATION
, "log" },
235 const struct table_entry
*paths
;
237 assert_cc(ELEMENTSOF(paths_system
) == ELEMENTSOF(paths_user
));
238 paths
= arg_user
? paths_user
: paths_system
;
240 i
= PTR_TO_UINT(data
);
241 assert(i
< ELEMENTSOF(paths_system
));
243 return sd_path_home(paths
[i
].type
, paths
[i
].suffix
, ret
);
246 static int log_unresolvable_specifier(const char *filename
, unsigned line
) {
247 static bool notified
= false;
249 /* In system mode, this is called when /etc is not fully initialized (e.g.
250 * in a chroot environment) where some specifiers are unresolvable. In user
251 * mode, this is called when some variables are not defined. These cases are
252 * not considered as an error so log at LOG_NOTICE only for the first time
253 * and then downgrade this to LOG_DEBUG for the rest. */
255 log_full(notified
? LOG_DEBUG
: LOG_NOTICE
,
256 "[%s:%u] Failed to resolve specifier: %s, skipping",
258 arg_user
? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
261 log_notice("All rules containing unresolvable specifiers will be skipped.");
267 static int user_config_paths(char*** ret
) {
268 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
269 _cleanup_free_
char *persistent_config
= NULL
, *runtime_config
= NULL
, *data_home
= NULL
;
270 _cleanup_strv_free_
char **res
= NULL
;
273 r
= xdg_user_dirs(&config_dirs
, &data_dirs
);
277 r
= xdg_user_config_dir(&persistent_config
, "/user-tmpfiles.d");
278 if (r
< 0 && r
!= -ENXIO
)
281 r
= xdg_user_runtime_dir(&runtime_config
, "/user-tmpfiles.d");
282 if (r
< 0 && r
!= -ENXIO
)
285 r
= xdg_user_data_dir(&data_home
, "/user-tmpfiles.d");
286 if (r
< 0 && r
!= -ENXIO
)
289 r
= strv_extend_strv_concat(&res
, config_dirs
, "/user-tmpfiles.d");
293 r
= strv_extend(&res
, persistent_config
);
297 r
= strv_extend(&res
, runtime_config
);
301 r
= strv_extend(&res
, data_home
);
305 r
= strv_extend_strv_concat(&res
, data_dirs
, "/user-tmpfiles.d");
309 r
= path_strv_make_absolute_cwd(res
);
318 static bool needs_glob(ItemType t
) {
322 IGNORE_DIRECTORY_PATH
,
324 RECURSIVE_REMOVE_PATH
,
328 RECURSIVE_RELABEL_PATH
,
334 RECURSIVE_SET_ATTRIBUTE
);
337 static bool takes_ownership(ItemType t
) {
345 CREATE_SUBVOLUME_INHERIT_QUOTA
,
346 CREATE_SUBVOLUME_NEW_QUOTA
,
354 IGNORE_DIRECTORY_PATH
,
356 RECURSIVE_REMOVE_PATH
);
359 static struct Item
* find_glob(OrderedHashmap
*h
, const char *match
) {
363 ORDERED_HASHMAP_FOREACH(j
, h
, i
) {
366 for (n
= 0; n
< j
->count
; n
++) {
367 Item
*item
= j
->items
+ n
;
369 if (fnmatch(item
->path
, match
, FNM_PATHNAME
|FNM_PERIOD
) == 0)
377 static void load_unix_sockets(void) {
378 _cleanup_fclose_
FILE *f
= NULL
;
384 /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
386 unix_sockets
= set_new(&path_hash_ops
);
392 f
= fopen("/proc/net/unix", "re");
394 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
, errno
,
395 "Failed to open /proc/net/unix, ignoring: %m");
400 r
= read_line(f
, LONG_LINE_MAX
, NULL
);
402 log_warning_errno(r
, "Failed to skip /proc/net/unix header line: %m");
406 log_warning("Premature end of file reading /proc/net/unix.");
411 _cleanup_free_
char *line
= NULL
;
414 r
= read_line(f
, LONG_LINE_MAX
, &line
);
416 log_warning_errno(r
, "Failed to read /proc/net/unix line, ignoring: %m");
419 if (r
== 0) /* EOF */
422 p
= strchr(line
, ':');
430 p
+= strspn(p
, WHITESPACE
);
431 p
+= strcspn(p
, WHITESPACE
); /* skip one more word */
432 p
+= strspn(p
, WHITESPACE
);
443 path_kill_slashes(s
);
445 r
= set_consume(unix_sockets
, s
);
446 if (r
< 0 && r
!= -EEXIST
) {
447 log_warning_errno(r
, "Failed to add AF_UNIX socket to set, ignoring: %m");
455 unix_sockets
= set_free_free(unix_sockets
);
458 static bool unix_socket_alive(const char *fn
) {
464 return !!set_get(unix_sockets
, (char*) fn
);
466 /* We don't know, so assume yes */
470 static int dir_is_mount_point(DIR *d
, const char *subdir
) {
472 int mount_id_parent
, mount_id
;
475 r_p
= name_to_handle_at_loop(dirfd(d
), ".", NULL
, &mount_id_parent
, 0);
479 r
= name_to_handle_at_loop(dirfd(d
), subdir
, NULL
, &mount_id
, 0);
483 /* got no handle; make no assumptions, return error */
484 if (r_p
< 0 && r
< 0)
487 /* got both handles; if they differ, it is a mount point */
488 if (r_p
>= 0 && r
>= 0)
489 return mount_id_parent
!= mount_id
;
491 /* got only one handle; assume different mount points if one
492 * of both queries was not supported by the filesystem */
493 if (IN_SET(r_p
, -ENOSYS
, -EOPNOTSUPP
) || IN_SET(r
, -ENOSYS
, -EOPNOTSUPP
))
502 static DIR* xopendirat_nomod(int dirfd
, const char *path
) {
505 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
|O_NOATIME
);
509 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
513 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
);
515 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
520 static DIR* opendir_nomod(const char *path
) {
521 return xopendirat_nomod(AT_FDCWD
, path
);
524 static int dir_cleanup(
528 const struct stat
*ds
,
533 bool keep_this_level
) {
536 struct timespec times
[2];
537 bool deleted
= false;
540 FOREACH_DIRENT_ALL(dent
, d
, break) {
543 _cleanup_free_
char *sub_path
= NULL
;
545 if (dot_or_dot_dot(dent
->d_name
))
548 if (fstatat(dirfd(d
), dent
->d_name
, &s
, AT_SYMLINK_NOFOLLOW
) < 0) {
552 /* FUSE, NFS mounts, SELinux might return EACCES */
553 r
= log_full_errno(errno
== EACCES
? LOG_DEBUG
: LOG_ERR
, errno
,
554 "stat(%s/%s) failed: %m", p
, dent
->d_name
);
558 /* Stay on the same filesystem */
559 if (s
.st_dev
!= rootdev
) {
560 log_debug("Ignoring \"%s/%s\": different filesystem.", p
, dent
->d_name
);
564 /* Try to detect bind mounts of the same filesystem instance; they
565 * do not differ in device major/minors. This type of query is not
566 * supported on all kernels or filesystem types though. */
567 if (S_ISDIR(s
.st_mode
) && dir_is_mount_point(d
, dent
->d_name
) > 0) {
568 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
573 sub_path
= strjoin(p
, "/", dent
->d_name
);
579 /* Is there an item configured for this path? */
580 if (ordered_hashmap_get(items
, sub_path
)) {
581 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path
);
585 if (find_glob(globs
, sub_path
)) {
586 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path
);
590 if (S_ISDIR(s
.st_mode
)) {
593 streq(dent
->d_name
, "lost+found") &&
595 log_debug("Ignoring \"%s\".", sub_path
);
600 log_warning("Reached max depth on \"%s\".", sub_path
);
602 _cleanup_closedir_
DIR *sub_dir
;
605 sub_dir
= xopendirat_nomod(dirfd(d
), dent
->d_name
);
608 r
= log_error_errno(errno
, "opendir(%s) failed: %m", sub_path
);
613 q
= dir_cleanup(i
, sub_path
, sub_dir
, &s
, cutoff
, rootdev
, false, maxdepth
-1, false);
618 /* Note: if you are wondering why we don't
619 * support the sticky bit for excluding
620 * directories from cleaning like we do it for
621 * other file system objects: well, the sticky
622 * bit already has a meaning for directories,
623 * so we don't want to overload that. */
625 if (keep_this_level
) {
626 log_debug("Keeping \"%s\".", sub_path
);
630 /* Ignore ctime, we change it when deleting */
631 age
= timespec_load(&s
.st_mtim
);
633 char a
[FORMAT_TIMESTAMP_MAX
];
634 /* Follows spelling in stat(1). */
635 log_debug("Directory \"%s\": modify time %s is too new.",
637 format_timestamp_us(a
, sizeof(a
), age
));
641 age
= timespec_load(&s
.st_atim
);
643 char a
[FORMAT_TIMESTAMP_MAX
];
644 log_debug("Directory \"%s\": access time %s is too new.",
646 format_timestamp_us(a
, sizeof(a
), age
));
650 log_debug("Removing directory \"%s\".", sub_path
);
651 if (unlinkat(dirfd(d
), dent
->d_name
, AT_REMOVEDIR
) < 0)
652 if (!IN_SET(errno
, ENOENT
, ENOTEMPTY
))
653 r
= log_error_errno(errno
, "rmdir(%s): %m", sub_path
);
656 /* Skip files for which the sticky bit is
657 * set. These are semantics we define, and are
658 * unknown elsewhere. See XDG_RUNTIME_DIR
659 * specification for details. */
660 if (s
.st_mode
& S_ISVTX
) {
661 log_debug("Skipping \"%s\": sticky bit set.", sub_path
);
665 if (mountpoint
&& S_ISREG(s
.st_mode
))
666 if (s
.st_uid
== 0 && STR_IN_SET(dent
->d_name
,
670 log_debug("Skipping \"%s\".", sub_path
);
674 /* Ignore sockets that are listed in /proc/net/unix */
675 if (S_ISSOCK(s
.st_mode
) && unix_socket_alive(sub_path
)) {
676 log_debug("Skipping \"%s\": live socket.", sub_path
);
680 /* Ignore device nodes */
681 if (S_ISCHR(s
.st_mode
) || S_ISBLK(s
.st_mode
)) {
682 log_debug("Skipping \"%s\": a device.", sub_path
);
686 /* Keep files on this level around if this is
688 if (keep_this_level
) {
689 log_debug("Keeping \"%s\".", sub_path
);
693 age
= timespec_load(&s
.st_mtim
);
695 char a
[FORMAT_TIMESTAMP_MAX
];
696 /* Follows spelling in stat(1). */
697 log_debug("File \"%s\": modify time %s is too new.",
699 format_timestamp_us(a
, sizeof(a
), age
));
703 age
= timespec_load(&s
.st_atim
);
705 char a
[FORMAT_TIMESTAMP_MAX
];
706 log_debug("File \"%s\": access time %s is too new.",
708 format_timestamp_us(a
, sizeof(a
), age
));
712 age
= timespec_load(&s
.st_ctim
);
714 char a
[FORMAT_TIMESTAMP_MAX
];
715 log_debug("File \"%s\": change time %s is too new.",
717 format_timestamp_us(a
, sizeof(a
), age
));
721 log_debug("unlink \"%s\"", sub_path
);
723 if (unlinkat(dirfd(d
), dent
->d_name
, 0) < 0)
725 r
= log_error_errno(errno
, "unlink(%s): %m", sub_path
);
734 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
];
736 /* Restore original directory timestamps */
737 times
[0] = ds
->st_atim
;
738 times
[1] = ds
->st_mtim
;
740 age1
= timespec_load(&ds
->st_atim
);
741 age2
= timespec_load(&ds
->st_mtim
);
742 log_debug("Restoring access and modification time on \"%s\": %s, %s",
744 format_timestamp_us(a
, sizeof(a
), age1
),
745 format_timestamp_us(b
, sizeof(b
), age2
));
746 if (futimens(dirfd(d
), times
) < 0)
747 log_error_errno(errno
, "utimensat(%s): %m", p
);
753 static bool dangerous_hardlinks(void) {
754 _cleanup_free_
char *value
= NULL
;
755 static int cached
= -1;
758 /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
759 * what the upstream default is. */
764 r
= read_one_line_file("/proc/sys/fs/protected_hardlinks", &value
);
766 log_debug_errno(r
, "Failed to read fs.protected_hardlinks sysctl: %m");
770 r
= parse_boolean(value
);
772 log_debug_errno(r
, "Failed to parse fs.protected_hardlinks sysctl: %m");
780 static bool hardlink_vulnerable(const struct stat
*st
) {
783 return !S_ISDIR(st
->st_mode
) && st
->st_nlink
> 1 && dangerous_hardlinks();
786 static int path_set_perms(Item
*i
, const char *path
) {
787 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
788 _cleanup_close_
int fd
= -1;
794 if (!i
->mode_set
&& !i
->uid_set
&& !i
->gid_set
)
797 /* We open the file with O_PATH here, to make the operation
798 * somewhat atomic. Also there's unfortunately no fchmodat()
799 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
802 fd
= open(path
, O_NOFOLLOW
|O_CLOEXEC
|O_PATH
);
804 int level
= LOG_ERR
, r
= -errno
;
806 /* Option "e" operates only on existing objects. Do not
807 * print errors about non-existent files or directories */
808 if (i
->type
== EMPTY_DIRECTORY
&& errno
== ENOENT
) {
813 log_full_errno(level
, errno
, "Adjusting owner and mode for %s failed: %m", path
);
817 if (fstat(fd
, &st
) < 0)
818 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
820 if (hardlink_vulnerable(&st
)) {
821 log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path
);
825 xsprintf(fn
, "/proc/self/fd/%i", fd
);
828 if (S_ISLNK(st
.st_mode
))
829 log_debug("Skipping mode fix for symlink %s.", path
);
834 if (!(st
.st_mode
& 0111))
836 if (!(st
.st_mode
& 0222))
838 if (!(st
.st_mode
& 0444))
840 if (!S_ISDIR(st
.st_mode
))
841 m
&= ~07000; /* remove sticky/sgid/suid bit, unless directory */
844 if (m
== (st
.st_mode
& 07777))
845 log_debug("\"%s\" has correct mode %o already.", path
, st
.st_mode
);
847 log_debug("Changing \"%s\" to mode %o.", path
, m
);
849 if (chmod(fn
, m
) < 0)
850 return log_error_errno(errno
, "chmod() of %s via %s failed: %m", path
, fn
);
855 if ((i
->uid_set
&& i
->uid
!= st
.st_uid
) ||
856 (i
->gid_set
&& i
->gid
!= st
.st_gid
)) {
857 log_debug("Changing \"%s\" to owner "UID_FMT
":"GID_FMT
,
859 i
->uid_set
? i
->uid
: UID_INVALID
,
860 i
->gid_set
? i
->gid
: GID_INVALID
);
863 i
->uid_set
? i
->uid
: UID_INVALID
,
864 i
->gid_set
? i
->gid
: GID_INVALID
) < 0)
865 return log_error_errno(errno
, "chown() of %s via %s failed: %m", path
, fn
);
871 return label_fix(path
, false, false);
874 static int parse_xattrs_from_arg(Item
*i
) {
884 _cleanup_free_
char *name
= NULL
, *value
= NULL
, *xattr
= NULL
;
886 r
= extract_first_word(&p
, &xattr
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
888 log_warning_errno(r
, "Failed to parse extended attribute '%s', ignoring: %m", p
);
892 r
= split_pair(xattr
, "=", &name
, &value
);
894 log_warning_errno(r
, "Failed to parse extended attribute, ignoring: %s", xattr
);
898 if (isempty(name
) || isempty(value
)) {
899 log_warning("Malformed extended attribute found, ignoring: %s", xattr
);
903 if (strv_push_pair(&i
->xattrs
, name
, value
) < 0)
912 static int path_set_xattrs(Item
*i
, const char *path
) {
913 char **name
, **value
;
918 STRV_FOREACH_PAIR(name
, value
, i
->xattrs
) {
919 log_debug("Setting extended attribute '%s=%s' on %s.", *name
, *value
, path
);
920 if (lsetxattr(path
, *name
, *value
, strlen(*value
), 0) < 0)
921 return log_error_errno(errno
, "Setting extended attribute %s=%s on %s failed: %m",
922 *name
, *value
, path
);
927 static int parse_acls_from_arg(Item
*item
) {
933 /* If force (= modify) is set, we will not modify the acl
934 * afterwards, so the mask can be added now if necessary. */
936 r
= parse_acl(item
->argument
, &item
->acl_access
, &item
->acl_default
, !item
->force
);
938 log_warning_errno(r
, "Failed to parse ACL \"%s\": %m. Ignoring", item
->argument
);
940 log_warning_errno(ENOSYS
, "ACLs are not supported. Ignoring");
947 static int path_set_acl(const char *path
, const char *pretty
, acl_type_t type
, acl_t acl
, bool modify
) {
948 _cleanup_(acl_free_charpp
) char *t
= NULL
;
949 _cleanup_(acl_freep
) acl_t dup
= NULL
;
952 /* Returns 0 for success, positive error if already warned,
953 * negative error otherwise. */
956 r
= acls_for_file(path
, type
, acl
, &dup
);
960 r
= calc_acl_mask_if_needed(&dup
);
968 /* the mask was already added earlier if needed */
971 r
= add_base_acls_if_needed(&dup
, path
);
975 t
= acl_to_any_text(dup
, NULL
, ',', TEXT_ABBREVIATE
);
976 log_debug("Setting %s ACL %s on %s.",
977 type
== ACL_TYPE_ACCESS
? "access" : "default",
980 r
= acl_set_file(path
, type
, dup
);
982 /* Return positive to indicate we already warned */
983 return -log_error_errno(errno
,
984 "Setting %s ACL \"%s\" on %s failed: %m",
985 type
== ACL_TYPE_ACCESS
? "access" : "default",
992 static int path_set_acls(Item
*item
, const char *path
) {
995 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
996 _cleanup_close_
int fd
= -1;
1002 fd
= open(path
, O_NOFOLLOW
|O_CLOEXEC
|O_PATH
);
1004 return log_error_errno(errno
, "Adjusting ACL of %s failed: %m", path
);
1006 if (fstat(fd
, &st
) < 0)
1007 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
1009 if (hardlink_vulnerable(&st
)) {
1010 log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path
);
1014 if (S_ISLNK(st
.st_mode
)) {
1015 log_debug("Skipping ACL fix for symlink %s.", path
);
1019 xsprintf(fn
, "/proc/self/fd/%i", fd
);
1021 if (item
->acl_access
)
1022 r
= path_set_acl(fn
, path
, ACL_TYPE_ACCESS
, item
->acl_access
, item
->force
);
1024 if (r
== 0 && item
->acl_default
)
1025 r
= path_set_acl(fn
, path
, ACL_TYPE_DEFAULT
, item
->acl_default
, item
->force
);
1028 return -r
; /* already warned */
1029 else if (r
== -EOPNOTSUPP
) {
1030 log_debug_errno(r
, "ACLs not supported by file system at %s", path
);
1033 log_error_errno(r
, "ACL operation on \"%s\" failed: %m", path
);
1038 #define ATTRIBUTES_ALL \
1047 FS_JOURNAL_DATA_FL | \
1054 static int parse_attribute_from_arg(Item
*item
) {
1056 static const struct {
1060 { 'A', FS_NOATIME_FL
}, /* do not update atime */
1061 { 'S', FS_SYNC_FL
}, /* Synchronous updates */
1062 { 'D', FS_DIRSYNC_FL
}, /* dirsync behaviour (directories only) */
1063 { 'a', FS_APPEND_FL
}, /* writes to file may only append */
1064 { 'c', FS_COMPR_FL
}, /* Compress file */
1065 { 'd', FS_NODUMP_FL
}, /* do not dump file */
1066 { 'e', FS_EXTENT_FL
}, /* Extents */
1067 { 'i', FS_IMMUTABLE_FL
}, /* Immutable file */
1068 { 'j', FS_JOURNAL_DATA_FL
}, /* Reserved for ext3 */
1069 { 's', FS_SECRM_FL
}, /* Secure deletion */
1070 { 'u', FS_UNRM_FL
}, /* Undelete */
1071 { 't', FS_NOTAIL_FL
}, /* file tail should not be merged */
1072 { 'T', FS_TOPDIR_FL
}, /* Top of directory hierarchies */
1073 { 'C', FS_NOCOW_FL
}, /* Do not cow file */
1082 unsigned value
= 0, mask
= 0;
1092 } else if (*p
== '-') {
1095 } else if (*p
== '=') {
1101 if (isempty(p
) && mode
!= MODE_SET
) {
1102 log_error("Setting file attribute on '%s' needs an attribute specification.", item
->path
);
1106 for (; p
&& *p
; p
++) {
1109 for (i
= 0; i
< ELEMENTSOF(attributes
); i
++)
1110 if (*p
== attributes
[i
].character
)
1113 if (i
>= ELEMENTSOF(attributes
)) {
1114 log_error("Unknown file attribute '%c' on '%s'.", *p
, item
->path
);
1118 v
= attributes
[i
].value
;
1120 SET_FLAG(value
, v
, IN_SET(mode
, MODE_ADD
, MODE_SET
));
1125 if (mode
== MODE_SET
)
1126 mask
|= ATTRIBUTES_ALL
;
1130 item
->attribute_mask
= mask
;
1131 item
->attribute_value
= value
;
1132 item
->attribute_set
= true;
1137 static int path_set_attribute(Item
*item
, const char *path
) {
1138 _cleanup_close_
int fd
= -1;
1143 if (!item
->attribute_set
|| item
->attribute_mask
== 0)
1146 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_CLOEXEC
|O_NOATIME
|O_NOFOLLOW
);
1149 return log_error_errno(errno
, "Skipping file attributes adjustment on symlink %s.", path
);
1151 return log_error_errno(errno
, "Cannot open '%s': %m", path
);
1154 if (fstat(fd
, &st
) < 0)
1155 return log_error_errno(errno
, "Cannot stat '%s': %m", path
);
1157 /* Issuing the file attribute ioctls on device nodes is not
1158 * safe, as that will be delivered to the drivers, not the
1159 * file system containing the device node. */
1160 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) {
1161 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path
);
1165 f
= item
->attribute_value
& item
->attribute_mask
;
1167 /* Mask away directory-specific flags */
1168 if (!S_ISDIR(st
.st_mode
))
1169 f
&= ~FS_DIRSYNC_FL
;
1171 r
= chattr_fd(fd
, f
, item
->attribute_mask
);
1173 log_full_errno(IN_SET(r
, -ENOTTY
, -EOPNOTSUPP
) ? LOG_DEBUG
: LOG_WARNING
,
1175 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
1176 path
, item
->attribute_value
, item
->attribute_mask
);
1181 static int write_one_file(Item
*i
, const char *path
) {
1182 _cleanup_close_
int fd
= -1;
1189 flags
= i
->type
== CREATE_FILE
? O_CREAT
|O_EXCL
|O_NOFOLLOW
:
1190 i
->type
== TRUNCATE_FILE
? O_CREAT
|O_TRUNC
|O_NOFOLLOW
: 0;
1192 RUN_WITH_UMASK(0000) {
1193 mac_selinux_create_file_prepare(path
, S_IFREG
);
1194 fd
= open(path
, flags
|O_NDELAY
|O_CLOEXEC
|O_WRONLY
|O_NOCTTY
, i
->mode
);
1195 mac_selinux_create_file_clear();
1199 if (i
->type
== WRITE_FILE
&& errno
== ENOENT
) {
1200 log_debug_errno(errno
, "Not writing missing file \"%s\": %m", path
);
1203 if (i
->type
== CREATE_FILE
&& errno
== EEXIST
) {
1204 log_debug_errno(errno
, "Not writing to pre-existing file \"%s\": %m", path
);
1209 if (!i
->argument
&& errno
== EROFS
&& stat(path
, &st
) == 0 &&
1210 (i
->type
== CREATE_FILE
|| st
.st_size
== 0))
1213 return log_error_errno(r
, "Failed to create file %s: %m", path
);
1217 log_debug("%s to \"%s\".", i
->type
== CREATE_FILE
? "Appending" : "Writing", path
);
1219 r
= loop_write(fd
, i
->argument
, strlen(i
->argument
), false);
1221 return log_error_errno(r
, "Failed to write file \"%s\": %m", path
);
1223 log_debug("\"%s\" has been created.", path
);
1225 fd
= safe_close(fd
);
1228 if (stat(path
, &st
) < 0)
1229 return log_error_errno(errno
, "stat(%s) failed: %m", path
);
1232 if (!S_ISREG(st
.st_mode
)) {
1233 log_error("%s is not a file.", path
);
1237 r
= path_set_perms(i
, path
);
1244 typedef int (*action_t
)(Item
*, const char *);
1246 static int item_do_children(Item
*i
, const char *path
, action_t action
) {
1247 _cleanup_closedir_
DIR *d
;
1254 /* This returns the first error we run into, but nevertheless
1257 d
= opendir_nomod(path
);
1259 return IN_SET(errno
, ENOENT
, ENOTDIR
, ELOOP
) ? 0 : -errno
;
1261 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
) {
1262 _cleanup_free_
char *p
= NULL
;
1265 if (dot_or_dot_dot(de
->d_name
))
1268 p
= strjoin(path
, "/", de
->d_name
);
1273 if (q
< 0 && q
!= -ENOENT
&& r
== 0)
1276 if (IN_SET(de
->d_type
, DT_UNKNOWN
, DT_DIR
)) {
1277 q
= item_do_children(i
, p
, action
);
1278 if (q
< 0 && r
== 0)
1286 static int glob_item(Item
*i
, action_t action
, bool recursive
) {
1287 _cleanup_globfree_ glob_t g
= {
1288 .gl_opendir
= (void *(*)(const char *)) opendir_nomod
,
1293 k
= safe_glob(i
->path
, GLOB_NOSORT
|GLOB_BRACE
, &g
);
1294 if (k
< 0 && k
!= -ENOENT
)
1295 return log_error_errno(k
, "glob(%s) failed: %m", i
->path
);
1297 STRV_FOREACH(fn
, g
.gl_pathv
) {
1299 if (k
< 0 && r
== 0)
1303 k
= item_do_children(i
, *fn
, action
);
1304 if (k
< 0 && r
== 0)
1317 _CREATION_MODE_INVALID
= -1
1320 static const char *creation_mode_verb_table
[_CREATION_MODE_MAX
] = {
1321 [CREATION_NORMAL
] = "Created",
1322 [CREATION_EXISTING
] = "Found existing",
1323 [CREATION_FORCE
] = "Created replacement",
1326 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb
, CreationMode
);
1328 static int create_item(Item
*i
) {
1332 CreationMode creation
;
1336 log_debug("Running create action for entry %c %s", (char) i
->type
, i
->path
);
1341 case IGNORE_DIRECTORY_PATH
:
1343 case RECURSIVE_REMOVE_PATH
:
1348 RUN_WITH_UMASK(0000)
1349 (void) mkdir_parents_label(i
->path
, 0755);
1351 r
= write_one_file(i
, i
->path
);
1358 RUN_WITH_UMASK(0000)
1359 (void) mkdir_parents_label(i
->path
, 0755);
1361 log_debug("Copying tree \"%s\" to \"%s\".", i
->argument
, i
->path
);
1362 r
= copy_tree(i
->argument
, i
->path
,
1363 i
->uid_set
? i
->uid
: UID_INVALID
,
1364 i
->gid_set
? i
->gid
: GID_INVALID
,
1367 if (r
== -EROFS
&& stat(i
->path
, &st
) == 0)
1374 return log_error_errno(r
, "Failed to copy files to %s: %m", i
->path
);
1376 if (stat(i
->argument
, &a
) < 0)
1377 return log_error_errno(errno
, "stat(%s) failed: %m", i
->argument
);
1379 if (stat(i
->path
, &b
) < 0)
1380 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1382 if ((a
.st_mode
^ b
.st_mode
) & S_IFMT
) {
1383 log_debug("Can't copy to %s, file exists already and is of different type", i
->path
);
1388 r
= path_set_perms(i
, i
->path
);
1395 r
= glob_item(i
, write_one_file
, false);
1401 case CREATE_DIRECTORY
:
1402 case TRUNCATE_DIRECTORY
:
1403 case CREATE_SUBVOLUME
:
1404 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1405 case CREATE_SUBVOLUME_NEW_QUOTA
:
1406 RUN_WITH_UMASK(0000)
1407 (void) mkdir_parents_label(i
->path
, 0755);
1409 if (IN_SET(i
->type
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
)) {
1411 if (btrfs_is_subvol(isempty(arg_root
) ? "/" : arg_root
) <= 0)
1413 /* Don't create a subvolume unless the
1414 * root directory is one, too. We do
1415 * this under the assumption that if
1416 * the root directory is just a plain
1417 * directory (i.e. very light-weight),
1418 * we shouldn't try to split it up
1419 * into subvolumes (i.e. more
1420 * heavy-weight). Thus, chroot()
1421 * environments and suchlike will get
1422 * a full brtfs subvolume set up below
1423 * their tree only if they
1424 * specifically set up a btrfs
1425 * subvolume for the root dir too. */
1429 RUN_WITH_UMASK((~i
->mode
) & 0777)
1430 r
= btrfs_subvol_make(i
->path
);
1435 if (IN_SET(i
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
) || r
== -ENOTTY
)
1436 RUN_WITH_UMASK(0000)
1437 r
= mkdir_label(i
->path
, i
->mode
);
1442 if (!IN_SET(r
, -EEXIST
, -EROFS
))
1443 return log_error_errno(r
, "Failed to create directory or subvolume \"%s\": %m", i
->path
);
1445 k
= is_dir(i
->path
, false);
1446 if (k
== -ENOENT
&& r
== -EROFS
)
1447 return log_error_errno(r
, "%s does not exist and cannot be created as the file system is read-only.", i
->path
);
1449 return log_error_errno(k
, "Failed to check if %s exists: %m", i
->path
);
1451 log_warning("\"%s\" already exists and is not a directory.", i
->path
);
1455 creation
= CREATION_EXISTING
;
1457 creation
= CREATION_NORMAL
;
1459 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1461 if (IN_SET(i
->type
, CREATE_SUBVOLUME_NEW_QUOTA
, CREATE_SUBVOLUME_INHERIT_QUOTA
)) {
1462 r
= btrfs_subvol_auto_qgroup(i
->path
, 0, i
->type
== CREATE_SUBVOLUME_NEW_QUOTA
);
1464 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i
->path
);
1465 else if (r
== -EROFS
)
1466 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i
->path
);
1467 else if (r
== -ENOPROTOOPT
)
1468 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i
->path
);
1470 q
= log_error_errno(r
, "Failed to adjust quota for subvolume \"%s\": %m", i
->path
);
1472 log_debug("Adjusted quota for subvolume \"%s\".", i
->path
);
1474 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i
->path
);
1478 case EMPTY_DIRECTORY
:
1479 r
= path_set_perms(i
, i
->path
);
1488 RUN_WITH_UMASK(0000) {
1489 (void) mkdir_parents_label(i
->path
, 0755);
1491 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1492 r
= mkfifo(i
->path
, i
->mode
);
1493 mac_selinux_create_file_clear();
1497 if (errno
!= EEXIST
)
1498 return log_error_errno(errno
, "Failed to create fifo %s: %m", i
->path
);
1500 if (lstat(i
->path
, &st
) < 0)
1501 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1503 if (!S_ISFIFO(st
.st_mode
)) {
1506 RUN_WITH_UMASK(0000) {
1507 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1508 r
= mkfifo_atomic(i
->path
, i
->mode
);
1509 mac_selinux_create_file_clear();
1513 return log_error_errno(r
, "Failed to create fifo %s: %m", i
->path
);
1514 creation
= CREATION_FORCE
;
1516 log_warning("\"%s\" already exists and is not a fifo.", i
->path
);
1520 creation
= CREATION_EXISTING
;
1522 creation
= CREATION_NORMAL
;
1523 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1525 r
= path_set_perms(i
, i
->path
);
1532 case CREATE_SYMLINK
: {
1533 RUN_WITH_UMASK(0000)
1534 (void) mkdir_parents_label(i
->path
, 0755);
1536 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1537 r
= symlink(i
->argument
, i
->path
);
1538 mac_selinux_create_file_clear();
1541 _cleanup_free_
char *x
= NULL
;
1543 if (errno
!= EEXIST
)
1544 return log_error_errno(errno
, "symlink(%s, %s) failed: %m", i
->argument
, i
->path
);
1546 r
= readlink_malloc(i
->path
, &x
);
1547 if (r
< 0 || !streq(i
->argument
, x
)) {
1550 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1551 r
= symlink_atomic(i
->argument
, i
->path
);
1552 mac_selinux_create_file_clear();
1554 if (IN_SET(r
, -EEXIST
, -ENOTEMPTY
)) {
1555 r
= rm_rf(i
->path
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
1557 return log_error_errno(r
, "rm -fr %s failed: %m", i
->path
);
1559 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1560 r
= symlink(i
->argument
, i
->path
) < 0 ? -errno
: 0;
1561 mac_selinux_create_file_clear();
1564 return log_error_errno(r
, "symlink(%s, %s) failed: %m", i
->argument
, i
->path
);
1566 creation
= CREATION_FORCE
;
1568 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i
->path
);
1572 creation
= CREATION_EXISTING
;
1575 creation
= CREATION_NORMAL
;
1576 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1580 case CREATE_BLOCK_DEVICE
:
1581 case CREATE_CHAR_DEVICE
: {
1584 if (have_effective_cap(CAP_MKNOD
) == 0) {
1585 /* In a container we lack CAP_MKNOD. We
1586 shouldn't attempt to create the device node in
1587 that case to avoid noise, and we don't support
1588 virtualized devices in containers anyway. */
1590 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i
->path
);
1594 RUN_WITH_UMASK(0000)
1595 (void) mkdir_parents_label(i
->path
, 0755);
1597 file_type
= i
->type
== CREATE_BLOCK_DEVICE
? S_IFBLK
: S_IFCHR
;
1599 RUN_WITH_UMASK(0000) {
1600 mac_selinux_create_file_prepare(i
->path
, file_type
);
1601 r
= mknod(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1602 mac_selinux_create_file_clear();
1606 if (errno
== EPERM
) {
1607 log_debug("We lack permissions, possibly because of cgroup configuration; "
1608 "skipping creation of device node %s.", i
->path
);
1612 if (errno
!= EEXIST
)
1613 return log_error_errno(errno
, "Failed to create device node %s: %m", i
->path
);
1615 if (lstat(i
->path
, &st
) < 0)
1616 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1618 if ((st
.st_mode
& S_IFMT
) != file_type
) {
1622 RUN_WITH_UMASK(0000) {
1623 mac_selinux_create_file_prepare(i
->path
, file_type
);
1624 r
= mknod_atomic(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1625 mac_selinux_create_file_clear();
1629 return log_error_errno(r
, "Failed to create device node \"%s\": %m", i
->path
);
1630 creation
= CREATION_FORCE
;
1632 log_debug("%s is not a device node.", i
->path
);
1636 creation
= CREATION_EXISTING
;
1638 creation
= CREATION_NORMAL
;
1640 log_debug("%s %s device node \"%s\" %u:%u.",
1641 creation_mode_verb_to_string(creation
),
1642 i
->type
== CREATE_BLOCK_DEVICE
? "block" : "char",
1643 i
->path
, major(i
->mode
), minor(i
->mode
));
1645 r
= path_set_perms(i
, i
->path
);
1654 r
= glob_item(i
, path_set_perms
, false);
1659 case RECURSIVE_RELABEL_PATH
:
1660 r
= glob_item(i
, path_set_perms
, true);
1666 r
= glob_item(i
, path_set_xattrs
, false);
1671 case RECURSIVE_SET_XATTR
:
1672 r
= glob_item(i
, path_set_xattrs
, true);
1678 r
= glob_item(i
, path_set_acls
, false);
1683 case RECURSIVE_SET_ACL
:
1684 r
= glob_item(i
, path_set_acls
, true);
1690 r
= glob_item(i
, path_set_attribute
, false);
1695 case RECURSIVE_SET_ATTRIBUTE
:
1696 r
= glob_item(i
, path_set_attribute
, true);
1705 static int remove_item_instance(Item
*i
, const char *instance
) {
1713 if (remove(instance
) < 0 && errno
!= ENOENT
)
1714 return log_error_errno(errno
, "rm(%s): %m", instance
);
1718 case TRUNCATE_DIRECTORY
:
1719 case RECURSIVE_REMOVE_PATH
:
1720 /* FIXME: we probably should use dir_cleanup() here
1721 * instead of rm_rf() so that 'x' is honoured. */
1722 log_debug("rm -rf \"%s\"", instance
);
1723 r
= rm_rf(instance
, (i
->type
== RECURSIVE_REMOVE_PATH
? REMOVE_ROOT
|REMOVE_SUBVOLUME
: 0) | REMOVE_PHYSICAL
);
1724 if (r
< 0 && r
!= -ENOENT
)
1725 return log_error_errno(r
, "rm_rf(%s): %m", instance
);
1730 assert_not_reached("wut?");
1736 static int remove_item(Item
*i
) {
1739 log_debug("Running remove action for entry %c %s", (char) i
->type
, i
->path
);
1744 case TRUNCATE_DIRECTORY
:
1745 case RECURSIVE_REMOVE_PATH
:
1746 return glob_item(i
, remove_item_instance
, false);
1753 static int clean_item_instance(Item
*i
, const char* instance
) {
1754 _cleanup_closedir_
DIR *d
= NULL
;
1758 char timestamp
[FORMAT_TIMESTAMP_MAX
];
1765 n
= now(CLOCK_REALTIME
);
1769 cutoff
= n
- i
->age
;
1771 d
= opendir_nomod(instance
);
1773 if (IN_SET(errno
, ENOENT
, ENOTDIR
)) {
1774 log_debug_errno(errno
, "Directory \"%s\": %m", instance
);
1778 return log_error_errno(errno
, "Failed to open directory %s: %m", instance
);
1781 if (fstat(dirfd(d
), &s
) < 0)
1782 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1784 if (!S_ISDIR(s
.st_mode
)) {
1785 log_error("%s is not a directory.", i
->path
);
1789 if (fstatat(dirfd(d
), "..", &ps
, AT_SYMLINK_NOFOLLOW
) != 0)
1790 return log_error_errno(errno
, "stat(%s/..) failed: %m", i
->path
);
1792 mountpoint
= s
.st_dev
!= ps
.st_dev
|| s
.st_ino
== ps
.st_ino
;
1794 log_debug("Cleanup threshold for %s \"%s\" is %s",
1795 mountpoint
? "mount point" : "directory",
1797 format_timestamp_us(timestamp
, sizeof(timestamp
), cutoff
));
1799 return dir_cleanup(i
, instance
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
,
1800 MAX_DEPTH
, i
->keep_first_level
);
1803 static int clean_item(Item
*i
) {
1806 log_debug("Running clean action for entry %c %s", (char) i
->type
, i
->path
);
1809 case CREATE_DIRECTORY
:
1810 case CREATE_SUBVOLUME
:
1811 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1812 case CREATE_SUBVOLUME_NEW_QUOTA
:
1813 case TRUNCATE_DIRECTORY
:
1816 clean_item_instance(i
, i
->path
);
1818 case EMPTY_DIRECTORY
:
1819 case IGNORE_DIRECTORY_PATH
:
1820 return glob_item(i
, clean_item_instance
, false);
1826 static int process_item_array(ItemArray
*array
);
1828 static int process_item(Item
*i
) {
1830 _cleanup_free_
char *prefix
= NULL
;
1839 prefix
= malloc(strlen(i
->path
) + 1);
1843 PATH_FOREACH_PREFIX(prefix
, i
->path
) {
1846 j
= ordered_hashmap_get(items
, prefix
);
1850 s
= process_item_array(j
);
1851 if (s
< 0 && t
== 0)
1856 if (chase_symlinks(i
->path
, NULL
, CHASE_NO_AUTOFS
, NULL
) == -EREMOTE
)
1859 r
= arg_create
? create_item(i
) : 0;
1860 q
= arg_remove
? remove_item(i
) : 0;
1861 p
= arg_clean
? clean_item(i
) : 0;
1869 static int process_item_array(ItemArray
*array
) {
1875 for (n
= 0; n
< array
->count
; n
++) {
1876 k
= process_item(array
->items
+ n
);
1877 if (k
< 0 && r
== 0)
1884 static void item_free_contents(Item
*i
) {
1888 strv_free(i
->xattrs
);
1891 acl_free(i
->acl_access
);
1892 acl_free(i
->acl_default
);
1896 static void item_array_free(ItemArray
*a
) {
1902 for (n
= 0; n
< a
->count
; n
++)
1903 item_free_contents(a
->items
+ n
);
1908 static int item_compare(const void *a
, const void *b
) {
1909 const Item
*x
= a
, *y
= b
;
1911 /* Make sure that the ownership taking item is put first, so
1912 * that we first create the node, and then can adjust it */
1914 if (takes_ownership(x
->type
) && !takes_ownership(y
->type
))
1916 if (!takes_ownership(x
->type
) && takes_ownership(y
->type
))
1919 return (int) x
->type
- (int) y
->type
;
1922 static bool item_compatible(Item
*a
, Item
*b
) {
1925 assert(streq(a
->path
, b
->path
));
1927 if (takes_ownership(a
->type
) && takes_ownership(b
->type
))
1928 /* check if the items are the same */
1929 return streq_ptr(a
->argument
, b
->argument
) &&
1931 a
->uid_set
== b
->uid_set
&&
1934 a
->gid_set
== b
->gid_set
&&
1937 a
->mode_set
== b
->mode_set
&&
1938 a
->mode
== b
->mode
&&
1940 a
->age_set
== b
->age_set
&&
1943 a
->mask_perms
== b
->mask_perms
&&
1945 a
->keep_first_level
== b
->keep_first_level
&&
1947 a
->major_minor
== b
->major_minor
;
1952 static bool should_include_path(const char *path
) {
1955 STRV_FOREACH(prefix
, arg_exclude_prefixes
)
1956 if (path_startswith(path
, *prefix
)) {
1957 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1962 STRV_FOREACH(prefix
, arg_include_prefixes
)
1963 if (path_startswith(path
, *prefix
)) {
1964 log_debug("Entry \"%s\" matches include prefix \"%s\".", path
, *prefix
);
1968 /* no matches, so we should include this path only if we
1969 * have no whitelist at all */
1970 if (strv_isempty(arg_include_prefixes
))
1973 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path
);
1977 static int specifier_expansion_from_arg(Item
*i
) {
1978 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
1984 if (i
->argument
== NULL
)
1989 case CREATE_SYMLINK
:
1993 r
= cunescape(i
->argument
, 0, &unescaped
);
1995 return log_error_errno(r
, "Failed to unescape parameter to write: %s", i
->argument
);
1997 r
= specifier_printf(unescaped
, specifier_table
, NULL
, &resolved
);
2001 free_and_replace(i
->argument
, resolved
);
2005 case RECURSIVE_SET_XATTR
:
2008 STRV_FOREACH (xattr
, i
->xattrs
) {
2009 r
= specifier_printf(*xattr
, specifier_table
, NULL
, &resolved
);
2013 free_and_replace(*xattr
, resolved
);
2023 static int parse_line(const char *fname
, unsigned line
, const char *buffer
, bool *invalid_config
) {
2025 _cleanup_free_
char *action
= NULL
, *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
, *path
= NULL
;
2026 _cleanup_(item_free_contents
) Item i
= {};
2027 ItemArray
*existing
;
2030 bool force
= false, boot
= false;
2036 r
= extract_many_words(
2048 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2049 /* invalid quoting and such or an unknown specifier */
2050 *invalid_config
= true;
2051 return log_error_errno(r
, "[%s:%u] Failed to parse line: %m", fname
, line
);
2055 *invalid_config
= true;
2056 log_error("[%s:%u] Syntax error.", fname
, line
);
2060 if (!isempty(buffer
) && !streq(buffer
, "-")) {
2061 i
.argument
= strdup(buffer
);
2066 if (isempty(action
)) {
2067 *invalid_config
= true;
2068 log_error("[%s:%u] Command too short '%s'.", fname
, line
, action
);
2072 for (pos
= 1; action
[pos
]; pos
++) {
2073 if (action
[pos
] == '!' && !boot
)
2075 else if (action
[pos
] == '+' && !force
)
2078 *invalid_config
= true;
2079 log_error("[%s:%u] Unknown modifiers in command '%s'",
2080 fname
, line
, action
);
2085 if (boot
&& !arg_boot
) {
2086 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
2094 r
= specifier_printf(path
, specifier_table
, NULL
, &i
.path
);
2096 return log_unresolvable_specifier(fname
, line
);
2098 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2099 *invalid_config
= true;
2100 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers: %s", fname
, line
, path
);
2105 case CREATE_DIRECTORY
:
2106 case CREATE_SUBVOLUME
:
2107 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
2108 case CREATE_SUBVOLUME_NEW_QUOTA
:
2109 case EMPTY_DIRECTORY
:
2110 case TRUNCATE_DIRECTORY
:
2113 case IGNORE_DIRECTORY_PATH
:
2115 case RECURSIVE_REMOVE_PATH
:
2118 case RECURSIVE_RELABEL_PATH
:
2120 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname
, line
, i
.type
);
2128 case CREATE_SYMLINK
:
2130 i
.argument
= strappend("/usr/share/factory/", i
.path
);
2138 *invalid_config
= true;
2139 log_error("[%s:%u] Write file requires argument.", fname
, line
);
2146 i
.argument
= strappend("/usr/share/factory/", i
.path
);
2149 } else if (!path_is_absolute(i
.argument
)) {
2150 *invalid_config
= true;
2151 log_error("[%s:%u] Source path is not absolute.", fname
, line
);
2155 path_kill_slashes(i
.argument
);
2158 case CREATE_CHAR_DEVICE
:
2159 case CREATE_BLOCK_DEVICE
: {
2160 unsigned major
, minor
;
2163 *invalid_config
= true;
2164 log_error("[%s:%u] Device file requires argument.", fname
, line
);
2168 if (sscanf(i
.argument
, "%u:%u", &major
, &minor
) != 2) {
2169 *invalid_config
= true;
2170 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
.argument
);
2174 i
.major_minor
= makedev(major
, minor
);
2179 case RECURSIVE_SET_XATTR
:
2181 *invalid_config
= true;
2182 log_error("[%s:%u] Set extended attribute requires argument.", fname
, line
);
2185 r
= parse_xattrs_from_arg(&i
);
2191 case RECURSIVE_SET_ACL
:
2193 *invalid_config
= true;
2194 log_error("[%s:%u] Set ACLs requires argument.", fname
, line
);
2197 r
= parse_acls_from_arg(&i
);
2203 case RECURSIVE_SET_ATTRIBUTE
:
2205 *invalid_config
= true;
2206 log_error("[%s:%u] Set file attribute requires argument.", fname
, line
);
2209 r
= parse_attribute_from_arg(&i
);
2210 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2211 *invalid_config
= true;
2217 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, (char) i
.type
);
2218 *invalid_config
= true;
2222 if (!path_is_absolute(i
.path
)) {
2223 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
.path
);
2224 *invalid_config
= true;
2228 path_kill_slashes(i
.path
);
2230 if (!should_include_path(i
.path
))
2233 r
= specifier_expansion_from_arg(&i
);
2235 return log_unresolvable_specifier(fname
, line
);
2237 if (IN_SET(r
, -EINVAL
, -EBADSLT
))
2238 *invalid_config
= true;
2239 return log_error_errno(r
, "[%s:%u] Failed to substitute specifiers in argument: %m",
2246 p
= prefix_root(arg_root
, i
.path
);
2254 if (!isempty(user
) && !streq(user
, "-")) {
2255 const char *u
= user
;
2257 r
= get_user_creds(&u
, &i
.uid
, NULL
, NULL
, NULL
);
2259 *invalid_config
= true;
2260 return log_error_errno(r
, "[%s:%u] Unknown user '%s'.", fname
, line
, user
);
2266 if (!isempty(group
) && !streq(group
, "-")) {
2267 const char *g
= group
;
2269 r
= get_group_creds(&g
, &i
.gid
);
2271 *invalid_config
= true;
2272 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
2279 if (!isempty(mode
) && !streq(mode
, "-")) {
2280 const char *mm
= mode
;
2284 i
.mask_perms
= true;
2288 if (parse_mode(mm
, &m
) < 0) {
2289 *invalid_config
= true;
2290 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
2297 i
.mode
= IN_SET(i
.type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
) ? 0755 : 0644;
2299 if (!isempty(age
) && !streq(age
, "-")) {
2300 const char *a
= age
;
2303 i
.keep_first_level
= true;
2307 if (parse_sec(a
, &i
.age
) < 0) {
2308 *invalid_config
= true;
2309 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
2316 h
= needs_glob(i
.type
) ? globs
: items
;
2318 existing
= ordered_hashmap_get(h
, i
.path
);
2322 for (n
= 0; n
< existing
->count
; n
++) {
2323 if (!item_compatible(existing
->items
+ n
, &i
)) {
2324 log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2325 fname
, line
, i
.path
);
2330 existing
= new0(ItemArray
, 1);
2334 r
= ordered_hashmap_put(h
, i
.path
, existing
);
2339 if (!GREEDY_REALLOC(existing
->items
, existing
->size
, existing
->count
+ 1))
2342 memcpy(existing
->items
+ existing
->count
++, &i
, sizeof(i
));
2344 /* Sort item array, to enforce stable ordering of application */
2345 qsort_safe(existing
->items
, existing
->count
, sizeof(Item
), item_compare
);
2351 static void help(void) {
2352 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2353 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2354 " -h --help Show this help\n"
2355 " --user Execute user configuration\n"
2356 " --version Show package version\n"
2357 " --create Create marked files/directories\n"
2358 " --clean Clean up marked directories\n"
2359 " --remove Remove marked files/directories\n"
2360 " --boot Execute actions only safe at boot\n"
2361 " --prefix=PATH Only apply rules with the specified prefix\n"
2362 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2363 " --root=PATH Operate on an alternate filesystem root\n"
2364 " --replace=PATH Treat arguments as replacement for PATH\n"
2365 , program_invocation_short_name
);
2368 static int parse_argv(int argc
, char *argv
[]) {
2371 ARG_VERSION
= 0x100,
2383 static const struct option options
[] = {
2384 { "help", no_argument
, NULL
, 'h' },
2385 { "user", no_argument
, NULL
, ARG_USER
},
2386 { "version", no_argument
, NULL
, ARG_VERSION
},
2387 { "create", no_argument
, NULL
, ARG_CREATE
},
2388 { "clean", no_argument
, NULL
, ARG_CLEAN
},
2389 { "remove", no_argument
, NULL
, ARG_REMOVE
},
2390 { "boot", no_argument
, NULL
, ARG_BOOT
},
2391 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
2392 { "exclude-prefix", required_argument
, NULL
, ARG_EXCLUDE_PREFIX
},
2393 { "root", required_argument
, NULL
, ARG_ROOT
},
2394 { "replace", required_argument
, NULL
, ARG_REPLACE
},
2403 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
2435 if (strv_push(&arg_include_prefixes
, optarg
) < 0)
2439 case ARG_EXCLUDE_PREFIX
:
2440 if (strv_push(&arg_exclude_prefixes
, optarg
) < 0)
2445 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
2451 if (!path_is_absolute(optarg
) ||
2452 !endswith(optarg
, ".conf")) {
2453 log_error("The argument to --replace= must an absolute path to a config file");
2457 arg_replace
= optarg
;
2464 assert_not_reached("Unhandled option");
2467 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
2468 log_error("You need to specify at least one of --clean, --create or --remove.");
2472 if (arg_replace
&& optind
>= argc
) {
2473 log_error("When --replace= is given, some configuration items must be specified");
2480 static int read_config_file(char **config_dirs
, const char *fn
, bool ignore_enoent
, bool *invalid_config
) {
2481 _cleanup_fclose_
FILE *_f
= NULL
;
2483 char line
[LINE_MAX
];
2491 if (streq(fn
, "-")) {
2492 log_debug("Reading config from stdin…");
2496 r
= search_and_fopen(fn
, "re", arg_root
, (const char**) config_dirs
, &_f
);
2498 if (ignore_enoent
&& r
== -ENOENT
) {
2499 log_debug_errno(r
, "Failed to open \"%s\", ignoring: %m", fn
);
2503 return log_error_errno(r
, "Failed to open '%s': %m", fn
);
2505 log_debug("Reading config file \"%s\"…", fn
);
2509 FOREACH_LINE(line
, f
, break) {
2512 bool invalid_line
= false;
2517 if (IN_SET(*l
, 0, '#'))
2520 k
= parse_line(fn
, v
, l
, &invalid_line
);
2523 /* Allow reporting with a special code if the caller requested this */
2524 *invalid_config
= true;
2526 /* The first error becomes our return value */
2531 /* we have to determine age parameter for each entry of type X */
2532 ORDERED_HASHMAP_FOREACH(i
, globs
, iterator
) {
2534 Item
*j
, *candidate_item
= NULL
;
2536 if (i
->type
!= IGNORE_DIRECTORY_PATH
)
2539 ORDERED_HASHMAP_FOREACH(j
, items
, iter
) {
2540 if (!IN_SET(j
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
))
2543 if (path_equal(j
->path
, i
->path
)) {
2548 if ((!candidate_item
&& path_startswith(i
->path
, j
->path
)) ||
2549 (candidate_item
&& path_startswith(j
->path
, candidate_item
->path
) && (fnmatch(i
->path
, j
->path
, FNM_PATHNAME
| FNM_PERIOD
) == 0)))
2553 if (candidate_item
&& candidate_item
->age_set
) {
2554 i
->age
= candidate_item
->age
;
2560 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
2568 static int parse_arguments(char **config_dirs
, char **args
, bool *invalid_config
) {
2572 STRV_FOREACH(arg
, args
) {
2573 r
= read_config_file(config_dirs
, *arg
, false, invalid_config
);
2581 static int read_config_files(char **config_dirs
, char **args
, bool *invalid_config
) {
2582 _cleanup_strv_free_
char **files
= NULL
;
2583 _cleanup_free_
char *p
= NULL
;
2587 r
= conf_files_list_strv(&files
, ".conf", arg_root
, 0, (const char* const*) config_dirs
);
2589 return log_error_errno(r
, "Failed to enumerate tmpfiles.d files: %m");
2592 r
= conf_files_insert(&files
, arg_root
, config_dirs
, arg_replace
);
2594 return log_error_errno(r
, "Failed to extend tmpfiles.d file list: %m");
2596 p
= path_join(arg_root
, arg_replace
, NULL
);
2601 STRV_FOREACH(f
, files
)
2602 if (p
&& path_equal(*f
, p
)) {
2603 log_debug("Parsing arguments at position \"%s\"…", *f
);
2605 r
= parse_arguments(config_dirs
, args
, invalid_config
);
2609 /* Just warn, ignore result otherwise.
2610 * read_config_file() has some debug output, so no need to print anything. */
2611 (void) read_config_file(config_dirs
, *f
, true, invalid_config
);
2616 int main(int argc
, char *argv
[]) {
2620 _cleanup_strv_free_
char **config_dirs
= NULL
;
2621 bool invalid_config
= false;
2623 r
= parse_argv(argc
, argv
);
2627 log_set_target(LOG_TARGET_AUTO
);
2628 log_parse_environment();
2635 items
= ordered_hashmap_new(&string_hash_ops
);
2636 globs
= ordered_hashmap_new(&string_hash_ops
);
2638 if (!items
|| !globs
) {
2646 r
= user_config_paths(&config_dirs
);
2648 log_error_errno(r
, "Failed to initialize configuration directory list: %m");
2652 config_dirs
= strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
2659 if (DEBUG_LOGGING
) {
2660 _cleanup_free_
char *t
= NULL
;
2662 t
= strv_join(config_dirs
, "\n\t");
2664 log_debug("Looking for configuration files in (higher priority first:\n\t%s", t
);
2667 /* If command line arguments are specified along with --replace, read all
2668 * configuration files and insert the positional arguments at the specified
2669 * place. Otherwise, if command line arguments are specified, execute just
2670 * them, and finally, without --replace= or any positional arguments, just
2671 * read configuration and execute it.
2673 if (arg_replace
|| optind
>= argc
)
2674 r
= read_config_files(config_dirs
, argv
+ optind
, &invalid_config
);
2676 r
= parse_arguments(config_dirs
, argv
+ optind
, &invalid_config
);
2682 /* The non-globbing ones usually create things, hence we apply
2684 ORDERED_HASHMAP_FOREACH(a
, items
, iterator
) {
2685 k
= process_item_array(a
);
2686 if (k
< 0 && r
== 0)
2690 /* The globbing ones usually alter things, hence we apply them
2692 ORDERED_HASHMAP_FOREACH(a
, globs
, iterator
) {
2693 k
= process_item_array(a
);
2694 if (k
< 0 && r
== 0)
2699 ordered_hashmap_free_with_destructor(items
, item_array_free
);
2700 ordered_hashmap_free_with_destructor(globs
, item_array_free
);
2702 free(arg_include_prefixes
);
2703 free(arg_exclude_prefixes
);
2706 set_free_free(unix_sockets
);
2708 mac_selinux_finish();
2711 return EXIT_FAILURE
;
2712 else if (invalid_config
)
2715 return EXIT_SUCCESS
;