2010-09-28 02:34:02 +02:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
2010-10-18 22:38:41 +02:00
Copyright 2010 Lennart Poettering , Kay Sievers
2015-01-09 08:00:37 +01:00
Copyright 2015 Zbigniew Jędrzejewski - Szmek
2010-09-28 02:34:02 +02:00
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 00:20:58 +02:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-09-28 02:34:02 +02:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 00:20:58 +02:00
Lesser General Public License for more details .
2010-09-28 02:34:02 +02:00
2012-04-12 00:20:58 +02:00
You should have received a copy of the GNU Lesser General Public License
2010-09-28 02:34:02 +02:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2015-09-23 03:01:06 +02:00
# include <dirent.h>
2010-09-28 02:34:02 +02:00
# include <errno.h>
2015-09-23 03:01:06 +02:00
# include <fcntl.h>
# include <fnmatch.h>
# include <getopt.h>
# include <glob.h>
2010-09-28 02:34:02 +02:00
# include <limits.h>
2015-09-23 03:01:06 +02:00
# include <linux/fs.h>
# include <stdbool.h>
# include <stddef.h>
2010-10-18 22:38:41 +02:00
# include <stdio.h>
# include <stdlib.h>
2015-09-23 03:01:06 +02:00
# include <string.h>
2015-01-09 07:10:02 +01:00
# include <sys/stat.h>
2014-12-04 10:32:10 +01:00
# include <sys/xattr.h>
2015-09-23 03:01:06 +02:00
# include <time.h>
# include <unistd.h>
2010-09-28 02:34:02 +02:00
2015-09-23 03:01:06 +02:00
# include "acl-util.h"
# include "btrfs-util.h"
2015-10-26 23:32:16 +01:00
# include "capability-util.h"
2015-10-26 20:39:23 +01:00
# include "chattr-util.h"
2015-09-23 03:01:06 +02:00
# include "conf-files.h"
# include "copy.h"
2015-10-23 18:52:53 +02:00
# include "escape.h"
2015-10-25 13:14:12 +01:00
# include "fd-util.h"
2015-10-26 18:05:03 +01:00
# include "fileio.h"
2015-09-23 03:01:06 +02:00
# include "formats-util.h"
2015-10-26 21:16:26 +01:00
# include "fs-util.h"
2015-10-27 01:48:17 +01:00
# include "glob-util.h"
2015-10-25 14:08:25 +01:00
# include "io-util.h"
2015-09-23 03:01:06 +02:00
# include "label.h"
2010-09-28 02:34:02 +02:00
# include "log.h"
2012-09-15 18:58:49 +02:00
# include "macro.h"
2013-03-14 01:20:54 +01:00
# include "missing.h"
2012-04-10 21:54:31 +02:00
# include "mkdir.h"
2015-10-26 16:18:16 +01:00
# include "parse-util.h"
2012-05-07 21:36:12 +02:00
# include "path-util.h"
2015-04-04 11:52:57 +02:00
# include "rm-rf.h"
2014-12-27 18:46:36 +01:00
# include "selinux-util.h"
2015-09-23 03:01:06 +02:00
# include "set.h"
# include "specifier.h"
2015-10-26 22:01:44 +01:00
# include "stat-util.h"
2015-10-27 01:26:31 +01:00
# include "stdio-util.h"
2015-10-26 22:31:05 +01:00
# include "string-table.h"
2015-10-24 22:58:24 +02:00
# include "string-util.h"
2015-09-23 03:01:06 +02:00
# include "strv.h"
2015-10-26 23:20:41 +01:00
# include "umask-util.h"
2015-10-25 22:32:30 +01:00
# include "user-util.h"
2015-09-23 03:01:06 +02:00
# include "util.h"
2010-09-28 02:34:02 +02:00
2010-09-29 10:08:24 +02:00
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
2010-09-28 02:34:02 +02:00
* them in the file system . This is intended to be used to create
2011-04-04 15:33:00 +02:00
* properly owned directories beneath / tmp , / var / tmp , / run , which are
* volatile and hence need to be recreated on bootup . */
2010-09-28 02:34:02 +02:00
2011-12-15 21:31:14 +01:00
typedef enum ItemType {
2011-02-12 09:31:25 +01:00
/* These ones take file names */
2010-10-18 22:38:41 +02:00
CREATE_FILE = ' f ' ,
TRUNCATE_FILE = ' F ' ,
CREATE_DIRECTORY = ' d ' ,
TRUNCATE_DIRECTORY = ' D ' ,
2014-12-27 18:46:36 +01:00
CREATE_SUBVOLUME = ' v ' ,
2015-10-21 19:46:23 +02:00
CREATE_SUBVOLUME_INHERIT_QUOTA = ' q ' ,
CREATE_SUBVOLUME_NEW_QUOTA = ' Q ' ,
2011-07-12 03:56:56 +02:00
CREATE_FIFO = ' p ' ,
2012-01-17 15:04:12 +01:00
CREATE_SYMLINK = ' L ' ,
CREATE_CHAR_DEVICE = ' c ' ,
CREATE_BLOCK_DEVICE = ' b ' ,
2014-06-10 23:02:40 +02:00
COPY_FILES = ' C ' ,
2011-02-12 09:31:25 +01:00
/* These ones take globs */
2015-04-10 16:22:22 +02:00
WRITE_FILE = ' w ' ,
2015-01-18 08:10:00 +01:00
SET_XATTR = ' t ' ,
RECURSIVE_SET_XATTR = ' T ' ,
SET_ACL = ' a ' ,
RECURSIVE_SET_ACL = ' A ' ,
2015-04-10 16:22:22 +02:00
SET_ATTRIBUTE = ' h ' ,
RECURSIVE_SET_ATTRIBUTE = ' H ' ,
2010-10-18 22:38:41 +02:00
IGNORE_PATH = ' x ' ,
2013-01-18 16:13:08 +01:00
IGNORE_DIRECTORY_PATH = ' X ' ,
2010-10-18 22:38:41 +02:00
REMOVE_PATH = ' r ' ,
2011-12-15 23:11:07 +01:00
RECURSIVE_REMOVE_PATH = ' R ' ,
2011-12-16 18:27:35 +01:00
RELABEL_PATH = ' z ' ,
2014-06-10 23:42:16 +02:00
RECURSIVE_RELABEL_PATH = ' Z ' ,
2015-04-10 16:22:22 +02:00
ADJUST_MODE = ' m ' , /* legacy, 'z' is identical to this */
2011-12-15 21:31:14 +01:00
} ItemType ;
2010-10-18 22:38:41 +02:00
typedef struct Item {
2011-12-15 21:31:14 +01:00
ItemType type ;
2010-10-18 22:38:41 +02:00
char * path ;
2012-01-17 15:04:12 +01:00
char * argument ;
2014-12-04 10:32:10 +01:00
char * * xattrs ;
2015-01-18 05:27:39 +01:00
# ifdef HAVE_ACL
acl_t acl_access ;
acl_t acl_default ;
# endif
2010-09-28 02:34:02 +02:00
uid_t uid ;
gid_t gid ;
2010-10-18 22:38:41 +02:00
mode_t mode ;
usec_t age ;
2012-01-17 15:04:12 +01:00
dev_t major_minor ;
2015-04-08 22:35:52 +02:00
unsigned attribute_value ;
unsigned attribute_mask ;
2012-01-17 15:04:12 +01:00
2010-10-18 22:38:41 +02:00
bool uid_set : 1 ;
bool gid_set : 1 ;
bool mode_set : 1 ;
bool age_set : 1 ;
2014-06-11 10:14:07 +02:00
bool mask_perms : 1 ;
2015-04-08 22:35:52 +02:00
bool attribute_set : 1 ;
2012-06-20 09:05:50 +02:00
bool keep_first_level : 1 ;
2014-06-11 01:37:35 +02:00
2014-06-16 13:21:07 +02:00
bool force : 1 ;
2014-06-11 01:37:35 +02:00
bool done : 1 ;
2010-10-18 22:38:41 +02:00
} Item ;
2015-01-09 08:00:37 +01:00
typedef struct ItemArray {
Item * items ;
size_t count ;
size_t size ;
} ItemArray ;
2010-10-18 22:38:41 +02:00
static bool arg_create = false ;
static bool arg_clean = false ;
static bool arg_remove = false ;
2013-12-30 19:00:38 +01:00
static bool arg_boot = false ;
2010-10-18 22:38:41 +02:00
2014-06-11 01:26:28 +02:00
static char * * arg_include_prefixes = NULL ;
static char * * arg_exclude_prefixes = NULL ;
2014-03-14 05:32:13 +01:00
static char * arg_root = NULL ;
2011-02-13 14:00:54 +01:00
2014-10-29 13:02:56 +01:00
static const char conf_file_dirs [ ] = CONF_DIRS_NULSTR ( " tmpfiles " ) ;
2012-06-09 04:31:19 +02:00
2010-10-18 22:38:41 +02:00
# define MAX_DEPTH 256
2015-04-22 18:18:56 +02:00
static OrderedHashmap * items = NULL , * globs = NULL ;
2014-06-11 01:26:28 +02:00
static Set * unix_sockets = NULL ;
2015-04-10 16:03:24 +02:00
static const Specifier specifier_table [ ] = {
{ ' m ' , specifier_machine_id , NULL } ,
{ ' b ' , specifier_boot_id , NULL } ,
{ ' H ' , specifier_host_name , NULL } ,
{ ' v ' , specifier_kernel_release , NULL } ,
{ }
} ;
2011-12-15 21:31:14 +01:00
static bool needs_glob ( ItemType t ) {
2014-06-10 22:50:46 +02:00
return IN_SET ( t ,
WRITE_FILE ,
IGNORE_PATH ,
IGNORE_DIRECTORY_PATH ,
REMOVE_PATH ,
RECURSIVE_REMOVE_PATH ,
2014-06-10 23:42:16 +02:00
ADJUST_MODE ,
2014-06-10 22:50:46 +02:00
RELABEL_PATH ,
2015-01-18 08:10:00 +01:00
RECURSIVE_RELABEL_PATH ,
SET_XATTR ,
RECURSIVE_SET_XATTR ,
SET_ACL ,
2015-04-09 13:12:06 +02:00
RECURSIVE_SET_ACL ,
SET_ATTRIBUTE ,
RECURSIVE_SET_ATTRIBUTE ) ;
2011-02-12 09:31:25 +01:00
}
2015-01-09 08:00:37 +01:00
static bool takes_ownership ( ItemType t ) {
return IN_SET ( t ,
CREATE_FILE ,
TRUNCATE_FILE ,
CREATE_DIRECTORY ,
TRUNCATE_DIRECTORY ,
CREATE_SUBVOLUME ,
2015-10-21 19:46:23 +02:00
CREATE_SUBVOLUME_INHERIT_QUOTA ,
CREATE_SUBVOLUME_NEW_QUOTA ,
2015-01-09 08:00:37 +01:00
CREATE_FIFO ,
CREATE_SYMLINK ,
CREATE_CHAR_DEVICE ,
CREATE_BLOCK_DEVICE ,
COPY_FILES ,
WRITE_FILE ,
IGNORE_PATH ,
IGNORE_DIRECTORY_PATH ,
REMOVE_PATH ,
RECURSIVE_REMOVE_PATH ) ;
}
2015-04-22 18:18:56 +02:00
static struct Item * find_glob ( OrderedHashmap * h , const char * match ) {
2015-01-09 08:00:37 +01:00
ItemArray * j ;
2011-02-12 09:31:25 +01:00
Iterator i ;
2015-04-22 18:18:56 +02:00
ORDERED_HASHMAP_FOREACH ( j , h , i ) {
2015-01-09 08:00:37 +01:00
unsigned n ;
for ( n = 0 ; n < j - > count ; n + + ) {
Item * item = j - > items + n ;
if ( fnmatch ( item - > path , match , FNM_PATHNAME | FNM_PERIOD ) = = 0 )
return item ;
}
}
2011-02-12 09:31:25 +01:00
return NULL ;
}
2011-02-14 21:55:06 +01:00
static void load_unix_sockets ( void ) {
2013-04-18 09:11:22 +02:00
_cleanup_fclose_ FILE * f = NULL ;
2011-02-14 21:55:06 +01:00
char line [ LINE_MAX ] ;
if ( unix_sockets )
return ;
/* We maintain a cache of the sockets we found in
* / proc / net / unix to speed things up a little . */
2014-08-13 01:00:18 +02:00
unix_sockets = set_new ( & string_hash_ops ) ;
2012-01-11 22:07:35 +01:00
if ( ! unix_sockets )
2011-02-14 21:55:06 +01:00
return ;
2012-01-11 22:07:35 +01:00
f = fopen ( " /proc/net/unix " , " re " ) ;
if ( ! f )
2011-02-14 21:55:06 +01:00
return ;
2012-01-11 22:07:35 +01:00
/* Skip header */
if ( ! fgets ( line , sizeof ( line ) , f ) )
2011-02-14 21:55:06 +01:00
goto fail ;
for ( ; ; ) {
char * p , * s ;
int k ;
2012-01-11 22:07:35 +01:00
if ( ! fgets ( line , sizeof ( line ) , f ) )
2011-02-14 21:55:06 +01:00
break ;
truncate_nl ( line ) ;
2012-01-11 22:07:35 +01:00
p = strchr ( line , ' : ' ) ;
if ( ! p )
continue ;
if ( strlen ( p ) < 37 )
2011-02-14 21:55:06 +01:00
continue ;
2012-01-11 22:07:35 +01:00
p + = 37 ;
2011-02-14 21:55:06 +01:00
p + = strspn ( p , WHITESPACE ) ;
2012-01-11 22:07:35 +01:00
p + = strcspn ( p , WHITESPACE ) ; /* skip one more word */
2011-02-14 21:55:06 +01:00
p + = strspn ( p , WHITESPACE ) ;
if ( * p ! = ' / ' )
continue ;
2012-01-11 22:07:35 +01:00
s = strdup ( p ) ;
if ( ! s )
2011-02-14 21:55:06 +01:00
goto fail ;
2011-02-17 13:13:34 +01:00
path_kill_slashes ( s ) ;
2013-04-23 05:12:15 +02:00
k = set_consume ( unix_sockets , s ) ;
if ( k < 0 & & k ! = - EEXIST )
goto fail ;
2011-02-14 21:55:06 +01:00
}
return ;
fail :
set_free_free ( unix_sockets ) ;
unix_sockets = NULL ;
}
static bool unix_socket_alive ( const char * fn ) {
assert ( fn ) ;
load_unix_sockets ( ) ;
if ( unix_sockets )
return ! ! set_get ( unix_sockets , ( char * ) fn ) ;
/* We don't know, so assume yes */
return true ;
}
2013-03-13 13:12:36 +01:00
static int dir_is_mount_point ( DIR * d , const char * subdir ) {
2014-06-10 22:50:46 +02:00
2015-01-07 16:28:04 +01:00
union file_handle_union h = FILE_HANDLE_INIT ;
2013-03-13 13:12:36 +01:00
int mount_id_parent , mount_id ;
int r_p , r ;
2014-04-19 19:22:35 +02:00
r_p = name_to_handle_at ( dirfd ( d ) , " . " , & h . handle , & mount_id_parent , 0 ) ;
2013-03-13 13:12:36 +01:00
if ( r_p < 0 )
r_p = - errno ;
2014-04-19 19:22:35 +02:00
h . handle . handle_bytes = MAX_HANDLE_SZ ;
r = name_to_handle_at ( dirfd ( d ) , subdir , & h . handle , & mount_id , 0 ) ;
2013-03-13 13:12:36 +01:00
if ( r < 0 )
r = - errno ;
/* got no handle; make no assumptions, return error */
if ( r_p < 0 & & r < 0 )
return r_p ;
/* got both handles; if they differ, it is a mount point */
if ( r_p > = 0 & & r > = 0 )
return mount_id_parent ! = mount_id ;
/* got only one handle; assume different mount points if one
* of both queries was not supported by the filesystem */
2014-10-12 02:35:06 +02:00
if ( r_p = = - ENOSYS | | r_p = = - EOPNOTSUPP | | r = = - ENOSYS | | r = = - EOPNOTSUPP )
2013-03-13 13:12:36 +01:00
return true ;
/* return error */
if ( r_p < 0 )
return r_p ;
return r ;
}
2014-01-02 06:02:31 +01:00
static DIR * xopendirat_nomod ( int dirfd , const char * path ) {
DIR * dir ;
dir = xopendirat ( dirfd , path , O_NOFOLLOW | O_NOATIME ) ;
2015-03-23 12:54:31 +01:00
if ( dir )
return dir ;
log_debug_errno ( errno , " Cannot open %sdirectory \" %s \" : %m " , dirfd = = AT_FDCWD ? " " : " sub " , path ) ;
if ( errno ! = EPERM )
return NULL ;
dir = xopendirat ( dirfd , path , O_NOFOLLOW ) ;
if ( ! dir )
log_debug_errno ( errno , " Cannot open %sdirectory \" %s \" : %m " , dirfd = = AT_FDCWD ? " " : " sub " , path ) ;
2014-01-02 06:02:31 +01:00
return dir ;
}
static DIR * opendir_nomod ( const char * path ) {
return xopendirat_nomod ( AT_FDCWD , path ) ;
}
2010-10-18 22:38:41 +02:00
static int dir_cleanup (
2013-01-18 16:13:08 +01:00
Item * i ,
2010-10-18 22:38:41 +02:00
const char * p ,
DIR * d ,
const struct stat * ds ,
usec_t cutoff ,
dev_t rootdev ,
bool mountpoint ,
2012-06-20 09:05:50 +02:00
int maxdepth ,
2013-09-17 23:33:30 +02:00
bool keep_this_level ) {
2010-10-18 22:38:41 +02:00
struct dirent * dent ;
struct timespec times [ 2 ] ;
bool deleted = false ;
int r = 0 ;
while ( ( dent = readdir ( d ) ) ) {
struct stat s ;
usec_t age ;
2013-04-18 09:11:22 +02:00
_cleanup_free_ char * sub_path = NULL ;
2010-10-18 22:38:41 +02:00
2015-01-23 05:35:34 +01:00
if ( STR_IN_SET ( dent - > d_name , " . " , " .. " ) )
2010-10-18 22:38:41 +02:00
continue ;
2010-09-28 02:34:02 +02:00
2010-10-18 22:38:41 +02:00
if ( fstatat ( dirfd ( d ) , dent - > d_name , & s , AT_SYMLINK_NOFOLLOW ) < 0 ) {
2013-10-17 03:20:46 +02:00
if ( errno = = ENOENT )
continue ;
2010-09-28 02:34:02 +02:00
2013-10-17 03:20:46 +02:00
/* FUSE, NFS mounts, SELinux might return EACCES */
if ( errno = = EACCES )
2014-11-28 19:29:59 +01:00
log_debug_errno ( errno , " stat(%s/%s) failed: %m " , p , dent - > d_name ) ;
2013-10-17 03:20:46 +02:00
else
2014-11-28 19:29:59 +01:00
log_error_errno ( errno , " stat(%s/%s) failed: %m " , p , dent - > d_name ) ;
2013-10-17 03:20:46 +02:00
r = - errno ;
2010-10-18 22:38:41 +02:00
continue ;
}
/* Stay on the same filesystem */
2015-01-24 07:54:05 +01:00
if ( s . st_dev ! = rootdev ) {
log_debug ( " Ignoring \" %s/%s \" : different filesystem. " , p , dent - > d_name ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
2013-03-13 13:12:36 +01:00
/* Try to detect bind mounts of the same filesystem instance; they
* do not differ in device major / minors . This type of query is not
* supported on all kernels or filesystem types though . */
2015-01-24 07:54:05 +01:00
if ( S_ISDIR ( s . st_mode ) & & dir_is_mount_point ( d , dent - > d_name ) > 0 ) {
log_debug ( " Ignoring \" %s/%s \" : different mount of the same filesystem. " ,
p , dent - > d_name ) ;
2013-03-13 13:12:36 +01:00
continue ;
2015-01-24 07:54:05 +01:00
}
2013-03-13 13:12:36 +01:00
2010-10-18 22:38:41 +02:00
/* Do not delete read-only files owned by root */
2015-01-24 07:54:05 +01:00
if ( s . st_uid = = 0 & & ! ( s . st_mode & S_IWUSR ) ) {
log_debug ( " Ignoring \" %s/%s \" : read-only and owner by root. " , p , dent - > d_name ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
2014-06-10 22:50:46 +02:00
sub_path = strjoin ( p , " / " , dent - > d_name , NULL ) ;
if ( ! sub_path ) {
2012-07-25 23:55:59 +02:00
r = log_oom ( ) ;
2010-10-18 22:38:41 +02:00
goto finish ;
}
/* Is there an item configured for this path? */
2015-04-22 18:18:56 +02:00
if ( ordered_hashmap_get ( items , sub_path ) ) {
2015-01-24 07:54:05 +01:00
log_debug ( " Ignoring \" %s \" : a separate entry exists. " , sub_path ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
2015-01-24 07:54:05 +01:00
if ( find_glob ( globs , sub_path ) ) {
log_debug ( " Ignoring \" %s \" : a separate glob exists. " , sub_path ) ;
2011-02-12 09:31:25 +01:00
continue ;
2015-01-24 07:54:05 +01:00
}
2011-02-12 09:31:25 +01:00
2010-10-18 22:38:41 +02:00
if ( S_ISDIR ( s . st_mode ) ) {
if ( mountpoint & &
streq ( dent - > d_name , " lost+found " ) & &
2015-01-24 07:54:05 +01:00
s . st_uid = = 0 ) {
log_debug ( " Ignoring \" %s \" . " , sub_path ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
if ( maxdepth < = 0 )
2015-01-24 07:54:05 +01:00
log_warning ( " Reached max depth on \" %s \" . " , sub_path ) ;
2010-10-18 22:38:41 +02:00
else {
2013-04-18 09:11:22 +02:00
_cleanup_closedir_ DIR * sub_dir ;
2010-10-18 22:38:41 +02:00
int q ;
2014-01-02 06:02:31 +01:00
sub_dir = xopendirat_nomod ( dirfd ( d ) , dent - > d_name ) ;
2014-06-10 22:50:46 +02:00
if ( ! sub_dir ) {
2015-01-24 07:54:05 +01:00
if ( errno ! = ENOENT )
r = log_error_errno ( errno , " opendir(%s) failed: %m " , sub_path ) ;
2010-10-18 22:38:41 +02:00
continue ;
}
2013-01-18 16:13:08 +01:00
q = dir_cleanup ( i , sub_path , sub_dir , & s , cutoff , rootdev , false , maxdepth - 1 , false ) ;
2010-10-18 22:38:41 +02:00
if ( q < 0 )
r = q ;
}
2012-06-20 09:05:50 +02:00
/* Note: if you are wondering why we don't
* support the sticky bit for excluding
* directories from cleaning like we do it for
* other file system objects : well , the sticky
* bit already has a meaning for directories ,
* so we don ' t want to overload that . */
2015-01-24 07:54:05 +01:00
if ( keep_this_level ) {
log_debug ( " Keeping \" %s \" . " , sub_path ) ;
2012-06-20 09:05:50 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2012-06-20 09:05:50 +02:00
2010-10-18 22:38:41 +02:00
/* Ignore ctime, we change it when deleting */
2015-01-24 07:54:05 +01:00
age = timespec_load ( & s . st_mtim ) ;
if ( age > = cutoff ) {
char a [ FORMAT_TIMESTAMP_MAX ] ;
/* Follows spelling in stat(1). */
log_debug ( " Directory \" %s \" : modify time %s is too new. " ,
sub_path ,
format_timestamp_us ( a , sizeof ( a ) , age ) ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
age = timespec_load ( & s . st_atim ) ;
if ( age > = cutoff ) {
char a [ FORMAT_TIMESTAMP_MAX ] ;
log_debug ( " Directory \" %s \" : access time %s is too new. " ,
sub_path ,
format_timestamp_us ( a , sizeof ( a ) , age ) ) ;
continue ;
}
2010-10-18 22:38:41 +02:00
2015-01-31 07:03:09 +01:00
log_debug ( " Removing directory \" %s \" . " , sub_path ) ;
if ( unlinkat ( dirfd ( d ) , dent - > d_name , AT_REMOVEDIR ) < 0 )
if ( errno ! = ENOENT & & errno ! = ENOTEMPTY ) {
log_error_errno ( errno , " rmdir(%s): %m " , sub_path ) ;
r = - errno ;
2010-10-18 22:38:41 +02:00
}
} else {
2010-11-14 20:12:51 +01:00
/* Skip files for which the sticky bit is
* set . These are semantics we define , and are
* unknown elsewhere . See XDG_RUNTIME_DIR
* specification for details . */
2015-01-24 07:54:05 +01:00
if ( s . st_mode & S_ISVTX ) {
log_debug ( " Skipping \" %s \" : sticky bit set. " , sub_path ) ;
2010-11-14 20:12:51 +01:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-11-14 20:12:51 +01:00
2015-01-24 07:54:05 +01:00
if ( mountpoint & & S_ISREG ( s . st_mode ) )
2015-06-13 19:14:37 +02:00
if ( s . st_uid = = 0 & & STR_IN_SET ( dent - > d_name ,
" .journal " ,
" aquota.user " ,
" aquota.group " ) ) {
2015-01-24 07:54:05 +01:00
log_debug ( " Skipping \" %s \" . " , sub_path ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
2011-02-14 21:55:06 +01:00
/* Ignore sockets that are listed in /proc/net/unix */
2015-01-24 07:54:05 +01:00
if ( S_ISSOCK ( s . st_mode ) & & unix_socket_alive ( sub_path ) ) {
log_debug ( " Skipping \" %s \" : live socket. " , sub_path ) ;
2011-02-14 21:55:06 +01:00
continue ;
2015-01-24 07:54:05 +01:00
}
2011-02-14 21:55:06 +01:00
2011-02-19 14:20:16 +01:00
/* Ignore device nodes */
2015-01-24 07:54:05 +01:00
if ( S_ISCHR ( s . st_mode ) | | S_ISBLK ( s . st_mode ) ) {
log_debug ( " Skipping \" %s \" : a device. " , sub_path ) ;
2011-02-19 14:20:16 +01:00
continue ;
2015-01-24 07:54:05 +01:00
}
2011-02-19 14:20:16 +01:00
2012-06-20 09:05:50 +02:00
/* Keep files on this level around if this is
* requested */
2015-01-24 07:54:05 +01:00
if ( keep_this_level ) {
log_debug ( " Keeping \" %s \" . " , sub_path ) ;
2012-06-20 09:05:50 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2012-06-20 09:05:50 +02:00
2015-01-24 07:54:05 +01:00
age = timespec_load ( & s . st_mtim ) ;
if ( age > = cutoff ) {
char a [ FORMAT_TIMESTAMP_MAX ] ;
/* Follows spelling in stat(1). */
log_debug ( " File \" %s \" : modify time %s is too new. " ,
sub_path ,
format_timestamp_us ( a , sizeof ( a ) , age ) ) ;
2010-10-18 22:38:41 +02:00
continue ;
2015-01-24 07:54:05 +01:00
}
2010-10-18 22:38:41 +02:00
2015-01-24 07:54:05 +01:00
age = timespec_load ( & s . st_atim ) ;
if ( age > = cutoff ) {
char a [ FORMAT_TIMESTAMP_MAX ] ;
log_debug ( " File \" %s \" : access time %s is too new. " ,
sub_path ,
format_timestamp_us ( a , sizeof ( a ) , age ) ) ;
continue ;
}
2010-10-18 22:38:41 +02:00
2015-01-24 07:54:05 +01:00
age = timespec_load ( & s . st_ctim ) ;
if ( age > = cutoff ) {
char a [ FORMAT_TIMESTAMP_MAX ] ;
log_debug ( " File \" %s \" : change time %s is too new. " ,
sub_path ,
format_timestamp_us ( a , sizeof ( a ) , age ) ) ;
continue ;
2010-10-18 22:38:41 +02:00
}
2015-01-24 07:54:05 +01:00
log_debug ( " unlink \" %s \" " , sub_path ) ;
if ( unlinkat ( dirfd ( d ) , dent - > d_name , 0 ) < 0 )
if ( errno ! = ENOENT )
r = log_error_errno ( errno , " unlink(%s): %m " , sub_path ) ;
2010-10-18 22:38:41 +02:00
deleted = true ;
}
}
finish :
if ( deleted ) {
2015-01-24 07:54:05 +01:00
usec_t age1 , age2 ;
char a [ FORMAT_TIMESTAMP_MAX ] , b [ FORMAT_TIMESTAMP_MAX ] ;
2010-10-18 22:38:41 +02:00
/* Restore original directory timestamps */
times [ 0 ] = ds - > st_atim ;
times [ 1 ] = ds - > st_mtim ;
2015-01-24 07:54:05 +01:00
age1 = timespec_load ( & ds - > st_atim ) ;
age2 = timespec_load ( & ds - > st_mtim ) ;
log_debug ( " Restoring access and modification time on \" %s \" : %s, %s " ,
p ,
format_timestamp_us ( a , sizeof ( a ) , age1 ) ,
format_timestamp_us ( b , sizeof ( b ) , age2 ) ) ;
2010-10-18 22:38:41 +02:00
if ( futimens ( dirfd ( d ) , times ) < 0 )
2014-11-28 19:29:59 +01:00
log_error_errno ( errno , " utimensat(%s): %m " , p ) ;
2010-10-18 22:38:41 +02:00
}
return r ;
}
2015-01-18 08:10:00 +01:00
static int path_set_perms ( Item * i , const char * path ) {
2015-04-13 15:16:54 +02:00
_cleanup_close_ int fd = - 1 ;
2014-08-17 09:45:00 +02:00
struct stat st ;
2014-06-10 22:50:46 +02:00
assert ( i ) ;
assert ( path ) ;
2015-04-13 15:16:54 +02:00
/* We open the file with O_PATH here, to make the operation
* somewhat atomic . Also there ' s unfortunately no fchmodat ( )
* with AT_SYMLINK_NOFOLLOW , hence we emulate it here via
* O_PATH . */
fd = open ( path , O_RDONLY | O_NOFOLLOW | O_CLOEXEC | O_PATH | O_NOATIME ) ;
if ( fd < 0 )
return log_error_errno ( errno , " Adjusting owner and mode for %s failed: %m " , path ) ;
if ( fstatat ( fd , " " , & st , AT_EMPTY_PATH ) < 0 )
return log_error_errno ( errno , " Failed to fstat() file % s : % m " , path) ;
2014-08-17 09:45:00 +02:00
2015-04-13 15:16:54 +02:00
if ( S_ISLNK ( st . st_mode ) )
log_debug ( " Skipping mode an owner fix for symlink %s. " , path ) ;
else {
char fn [ strlen ( " /proc/self/fd/ " ) + DECIMAL_STR_MAX ( int ) ] ;
xsprintf ( fn , " /proc/self/fd/%i " , fd ) ;
2014-06-11 10:14:07 +02:00
2015-04-13 15:16:54 +02:00
/* not using i->path directly because it may be a glob */
if ( i - > mode_set ) {
mode_t m = i - > mode ;
if ( i - > mask_perms ) {
if ( ! ( st . st_mode & 0111 ) )
m & = ~ 0111 ;
if ( ! ( st . st_mode & 0222 ) )
2015-08-06 00:31:09 +02:00
m & = ~ 0222 ;
2015-04-13 15:16:54 +02:00
if ( ! ( st . st_mode & 0444 ) )
m & = ~ 0444 ;
if ( ! S_ISDIR ( st . st_mode ) )
m & = ~ 07000 ; /* remove sticky/sgid/suid bit, unless directory */
}
2014-06-11 10:14:07 +02:00
2015-04-13 15:16:54 +02:00
if ( m = = ( st . st_mode & 07777 ) )
log_debug ( " \" %s \" has right mode %o " , path , st . st_mode ) ;
else {
log_debug ( " chmod \" %s \" to mode %o " , path , m ) ;
if ( chmod ( fn , m ) < 0 )
return log_error_errno ( errno , " chmod(%s) failed : % m " , path) ;
}
2011-12-16 18:00:11 +01:00
}
2015-04-13 15:16:54 +02:00
if ( ( i - > uid ! = st . st_uid | | i - > gid ! = st . st_gid ) & &
( i - > uid_set | | i - > gid_set ) ) {
log_debug ( " chown \" %s \" to " UID_FMT " . " GID_FMT ,
path ,
i - > uid_set ? i - > uid : UID_INVALID ,
i - > gid_set ? i - > gid : GID_INVALID ) ;
if ( chown ( fn ,
i - > uid_set ? i - > uid : UID_INVALID ,
i - > gid_set ? i - > gid : GID_INVALID ) < 0 )
2015-08-06 00:31:09 +02:00
return log_error_errno ( errno , " chown(%s) failed : % m " , path) ;
2015-04-13 15:16:54 +02:00
}
2015-01-24 07:54:05 +01:00
}
2011-12-16 18:00:11 +01:00
2015-04-13 15:16:54 +02:00
fd = safe_close ( fd ) ;
2014-06-11 09:19:57 +02:00
return label_fix ( path , false , false ) ;
2011-12-16 18:00:11 +01:00
}
2015-04-10 16:03:24 +02:00
static int parse_xattrs_from_arg ( Item * i ) {
2014-12-04 10:32:10 +01:00
const char * p ;
int r ;
assert ( i ) ;
2015-01-09 07:10:02 +01:00
assert ( i - > argument ) ;
2014-12-04 10:32:10 +01:00
p = i - > argument ;
2015-03-23 12:55:36 +01:00
for ( ; ; ) {
2015-04-10 16:03:24 +02:00
_cleanup_free_ char * name = NULL , * value = NULL , * xattr = NULL , * xattr_replaced = NULL ;
2015-03-23 12:55:36 +01:00
2015-06-23 18:26:49 +02:00
r = extract_first_word ( & p , & xattr , NULL , EXTRACT_QUOTES | EXTRACT_CUNESCAPE ) ;
2015-03-23 12:55:36 +01:00
if ( r < 0 )
2015-04-10 16:03:24 +02:00
log_warning_errno ( r , " Failed to parse extended attribute '%s', ignoring: %m " , p ) ;
2015-03-23 12:55:36 +01:00
if ( r < = 0 )
break ;
2015-01-09 07:10:02 +01:00
2015-04-10 16:03:24 +02:00
r = specifier_printf ( xattr , specifier_table , NULL , & xattr_replaced ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to replace specifiers in extended attribute '%s': %m " , xattr ) ;
r = split_pair ( xattr_replaced , " = " , & name , & value ) ;
2014-12-04 10:32:10 +01:00
if ( r < 0 ) {
2015-03-23 12:55:36 +01:00
log_warning_errno ( r , " Failed to parse extended attribute, ignoring: %s " , xattr ) ;
2014-12-04 10:32:10 +01:00
continue ;
}
2015-01-09 07:10:02 +01:00
2015-03-23 12:55:36 +01:00
if ( isempty ( name ) | | isempty ( value ) ) {
2015-04-10 16:03:24 +02:00
log_warning ( " Malformed extended attribute found, ignoring: %s " , xattr ) ;
2014-12-04 10:32:10 +01:00
continue ;
}
2015-01-09 07:10:02 +01:00
2015-03-23 12:55:36 +01:00
if ( strv_push_pair ( & i - > xattrs , name , value ) < 0 )
2014-12-04 10:32:10 +01:00
return log_oom ( ) ;
2015-01-09 07:10:02 +01:00
2015-03-23 12:55:36 +01:00
name = value = NULL ;
2014-12-04 10:32:10 +01:00
}
2015-03-23 12:55:36 +01:00
return 0 ;
2014-12-04 10:32:10 +01:00
}
2015-01-18 08:10:00 +01:00
static int path_set_xattrs ( Item * i , const char * path ) {
2014-12-04 10:32:10 +01:00
char * * name , * * value ;
assert ( i ) ;
assert ( path ) ;
STRV_FOREACH_PAIR ( name , value , i - > xattrs ) {
int n ;
2015-01-09 07:10:02 +01:00
2014-12-04 10:32:10 +01:00
n = strlen ( * value ) ;
2015-04-10 16:03:24 +02:00
log_debug ( " Setting extended attribute '%s=%s' on %s. " , * name , * value , path ) ;
2014-12-04 10:32:10 +01:00
if ( lsetxattr ( path , * name , * value , n , 0 ) < 0 ) {
2015-04-10 16:03:24 +02:00
log_error ( " Setting extended attribute %s=%s on %s failed: %m " , * name , * value , path ) ;
2014-12-04 10:32:10 +01:00
return - errno ;
}
}
return 0 ;
}
2015-04-10 16:03:24 +02:00
static int parse_acls_from_arg ( Item * item ) {
2015-01-18 05:27:39 +01:00
# ifdef HAVE_ACL
int r ;
assert ( item ) ;
2015-01-18 11:02:47 +01:00
/* If force (= modify) is set, we will not modify the acl
* afterwards , so the mask can be added now if necessary . */
2015-04-10 16:03:24 +02:00
2015-01-18 11:02:47 +01:00
r = parse_acl ( item - > argument , & item - > acl_access , & item - > acl_default , ! item - > force ) ;
2015-01-18 05:27:39 +01:00
if ( r < 0 )
2015-03-23 12:55:36 +01:00
log_warning_errno ( r , " Failed to parse ACL \" %s \" : %m. Ignoring " , item - > argument ) ;
2015-01-18 05:27:39 +01:00
# else
log_warning_errno ( ENOSYS , " ACLs are not supported. Ignoring " ) ;
# endif
return 0 ;
}
2015-02-03 02:28:39 +01:00
# ifdef HAVE_ACL
2015-04-13 15:16:54 +02:00
static int path_set_acl ( const char * path , const char * pretty , acl_type_t type , acl_t acl , bool modify ) {
_cleanup_ ( acl_free_charpp ) char * t = NULL ;
2015-01-19 00:22:27 +01:00
_cleanup_ ( acl_freep ) acl_t dup = NULL ;
2015-01-18 11:02:47 +01:00
int r ;
2015-03-03 00:35:08 +01:00
/* Returns 0 for success, positive error if already warned,
* negative error otherwise . */
2015-01-18 11:02:47 +01:00
if ( modify ) {
2015-01-19 00:22:27 +01:00
r = acls_for_file ( path , type , acl , & dup ) ;
2015-01-18 11:02:47 +01:00
if ( r < 0 )
return r ;
2015-01-19 00:22:27 +01:00
r = calc_acl_mask_if_needed ( & dup ) ;
if ( r < 0 )
return r ;
} else {
dup = acl_dup ( acl ) ;
if ( ! dup )
return - errno ;
/* the mask was already added earlier if needed */
}
r = add_base_acls_if_needed ( & dup , path ) ;
if ( r < 0 )
return r ;
2015-01-24 07:54:05 +01:00
t = acl_to_any_text ( dup , NULL , ' , ' , TEXT_ABBREVIATE ) ;
2015-04-13 15:16:54 +02:00
log_debug ( " Setting %s ACL %s on %s. " ,
2015-01-24 07:54:05 +01:00
type = = ACL_TYPE_ACCESS ? " access " : " default " ,
2015-04-13 15:16:54 +02:00
strna ( t ) , pretty ) ;
2015-01-18 11:02:47 +01:00
2015-01-24 07:54:05 +01:00
r = acl_set_file ( path , type , dup ) ;
if ( r < 0 )
2015-04-10 14:44:52 +02:00
/* Return positive to indicate we already warned */
return - log_error_errno ( errno ,
" Setting %s ACL \" %s \" on %s failed: %m " ,
type = = ACL_TYPE_ACCESS ? " access " : " default " ,
2015-04-13 15:16:54 +02:00
strna ( t ) , pretty ) ;
2015-03-03 00:35:08 +01:00
2015-01-24 07:54:05 +01:00
return 0 ;
2015-01-18 11:02:47 +01:00
}
2015-02-03 02:28:39 +01:00
# endif
2015-01-18 11:02:47 +01:00
2015-01-18 08:10:00 +01:00
static int path_set_acls ( Item * item , const char * path ) {
2015-03-03 00:35:08 +01:00
int r = 0 ;
2015-01-18 05:27:39 +01:00
# ifdef HAVE_ACL
2015-04-13 15:16:54 +02:00
char fn [ strlen ( " /proc/self/fd/ " ) + DECIMAL_STR_MAX ( int ) ] ;
_cleanup_close_ int fd = - 1 ;
struct stat st ;
2015-01-18 05:27:39 +01:00
assert ( item ) ;
assert ( path ) ;
2015-04-13 15:16:54 +02:00
fd = open ( path , O_RDONLY | O_NOFOLLOW | O_CLOEXEC | O_PATH | O_NOATIME ) ;
if ( fd < 0 )
return log_error_errno ( errno , " Adjusting ACL of %s failed: %m " , path ) ;
if ( fstatat ( fd , " " , & st , AT_EMPTY_PATH ) < 0 )
return log_error_errno ( errno , " Failed to fstat() file % s : % m " , path) ;
if ( S_ISLNK ( st . st_mode ) ) {
log_debug ( " Skipping ACL fix for symlink %s. " , path ) ;
return 0 ;
}
xsprintf ( fn , " /proc/self/fd/%i " , fd ) ;
2015-03-03 00:35:08 +01:00
if ( item - > acl_access )
2015-04-13 15:16:54 +02:00
r = path_set_acl ( fn , path , ACL_TYPE_ACCESS , item - > acl_access , item - > force ) ;
2015-01-18 05:27:39 +01:00
2015-03-03 00:35:08 +01:00
if ( r = = 0 & & item - > acl_default )
2015-04-13 15:16:54 +02:00
r = path_set_acl ( fn , path , ACL_TYPE_DEFAULT , item - > acl_default , item - > force ) ;
2015-01-18 05:27:39 +01:00
2015-03-03 00:35:08 +01:00
if ( r > 0 )
return - r ; /* already warned */
2015-03-13 14:08:00 +01:00
else if ( r = = - EOPNOTSUPP ) {
2015-03-03 00:35:08 +01:00
log_debug_errno ( r , " ACLs not supported by file system at %s " , path ) ;
return 0 ;
} else if ( r < 0 )
log_error_errno ( r , " ACL operation on \" %s \" failed: %m " , path ) ;
# endif
return r ;
2015-01-18 05:27:39 +01:00
}
2015-04-08 22:35:52 +02:00
# define ATTRIBUTES_ALL \
( FS_NOATIME_FL | \
FS_SYNC_FL | \
FS_DIRSYNC_FL | \
FS_APPEND_FL | \
FS_COMPR_FL | \
FS_NODUMP_FL | \
FS_EXTENT_FL | \
FS_IMMUTABLE_FL | \
FS_JOURNAL_DATA_FL | \
FS_SECRM_FL | \
FS_UNRM_FL | \
FS_NOTAIL_FL | \
FS_TOPDIR_FL | \
FS_NOCOW_FL )
2015-04-10 16:03:24 +02:00
static int parse_attribute_from_arg ( Item * item ) {
2015-04-08 22:35:52 +02:00
static const struct {
char character ;
unsigned value ;
} attributes [ ] = {
{ ' A ' , FS_NOATIME_FL } , /* do not update atime */
{ ' S ' , FS_SYNC_FL } , /* Synchronous updates */
{ ' D ' , FS_DIRSYNC_FL } , /* dirsync behaviour (directories only) */
{ ' a ' , FS_APPEND_FL } , /* writes to file may only append */
{ ' c ' , FS_COMPR_FL } , /* Compress file */
{ ' d ' , FS_NODUMP_FL } , /* do not dump file */
{ ' e ' , FS_EXTENT_FL } , /* Top of directory hierarchies*/
{ ' i ' , FS_IMMUTABLE_FL } , /* Immutable file */
{ ' j ' , FS_JOURNAL_DATA_FL } , /* Reserved for ext3 */
{ ' s ' , FS_SECRM_FL } , /* Secure deletion */
{ ' u ' , FS_UNRM_FL } , /* Undelete */
{ ' t ' , FS_NOTAIL_FL } , /* file tail should not be merged */
{ ' T ' , FS_TOPDIR_FL } , /* Top of directory hierarchies*/
{ ' C ' , FS_NOCOW_FL } , /* Do not cow file */
2015-03-16 20:33:50 +01:00
} ;
2015-04-08 22:35:52 +02:00
2015-03-16 20:33:50 +01:00
enum {
MODE_ADD ,
MODE_DEL ,
MODE_SET
} mode = MODE_ADD ;
2015-04-08 22:35:52 +02:00
unsigned value = 0 , mask = 0 ;
const char * p ;
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
assert ( item ) ;
p = item - > argument ;
if ( p ) {
if ( * p = = ' + ' ) {
mode = MODE_ADD ;
p + + ;
} else if ( * p = = ' - ' ) {
mode = MODE_DEL ;
p + + ;
} else if ( * p = = ' = ' ) {
mode = MODE_SET ;
p + + ;
}
2015-03-16 20:33:50 +01:00
}
2015-04-08 22:35:52 +02:00
if ( isempty ( p ) & & mode ! = MODE_SET ) {
log_error ( " Setting file attribute on '%s' needs an attribute specification. " , item - > path ) ;
2015-03-16 20:33:50 +01:00
return - EINVAL ;
}
2015-04-08 22:35:52 +02:00
for ( ; p & & * p ; p + + ) {
unsigned i , v ;
for ( i = 0 ; i < ELEMENTSOF ( attributes ) ; i + + )
if ( * p = = attributes [ i ] . character )
break ;
if ( i > = ELEMENTSOF ( attributes ) ) {
log_error ( " Unknown file attribute '%c' on '%s'. " , * p , item - > path ) ;
2015-03-16 20:33:50 +01:00
return - EINVAL ;
}
2015-04-08 22:35:52 +02:00
v = attributes [ i ] . value ;
2015-03-16 20:33:50 +01:00
if ( mode = = MODE_ADD | | mode = = MODE_SET )
2015-04-08 22:35:52 +02:00
value | = v ;
2015-03-16 20:33:50 +01:00
else
2015-04-08 22:35:52 +02:00
value & = ~ v ;
mask | = v ;
2015-03-16 20:33:50 +01:00
}
if ( mode = = MODE_SET )
2015-04-08 22:35:52 +02:00
mask | = ATTRIBUTES_ALL ;
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
assert ( mask ! = 0 ) ;
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
item - > attribute_mask = mask ;
item - > attribute_value = value ;
item - > attribute_set = true ;
2015-03-16 20:33:50 +01:00
return 0 ;
}
2015-04-08 22:35:52 +02:00
static int path_set_attribute ( Item * item , const char * path ) {
2015-03-16 20:33:50 +01:00
_cleanup_close_ int fd = - 1 ;
struct stat st ;
2015-04-08 22:35:52 +02:00
unsigned f ;
int r ;
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
if ( ! item - > attribute_set | | item - > attribute_mask = = 0 )
2015-03-16 20:33:50 +01:00
return 0 ;
2015-04-13 15:16:54 +02:00
fd = open ( path , O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOATIME | O_NOFOLLOW ) ;
if ( fd < 0 ) {
if ( errno = = ELOOP )
return log_error_errno ( errno , " Skipping file attributes adjustment on symlink %s. " , path ) ;
2015-04-08 22:35:52 +02:00
return log_error_errno ( errno , " Cannot open '%s': %m " , path ) ;
2015-04-13 15:16:54 +02:00
}
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
if ( fstat ( fd , & st ) < 0 )
return log_error_errno ( errno , " Cannot stat '%s': %m " , path ) ;
/* Issuing the file attribute ioctls on device nodes is not
* safe , as that will be delivered to the drivers , not the
* file system containing the device node . */
if ( ! S_ISREG ( st . st_mode ) & & ! S_ISDIR ( st . st_mode ) ) {
log_error ( " Setting file flags is only supported on regular files and directories, cannot set on '%s'. " , path ) ;
return - EINVAL ;
}
f = item - > attribute_value & item - > attribute_mask ;
/* Mask away directory-specific flags */
2015-03-16 20:33:50 +01:00
if ( ! S_ISDIR ( st . st_mode ) )
f & = ~ FS_DIRSYNC_FL ;
2015-04-08 22:35:52 +02:00
r = chattr_fd ( fd , f , item - > attribute_mask ) ;
2015-03-16 20:33:50 +01:00
if ( r < 0 )
2015-07-22 22:02:14 +02:00
log_full_errno ( r = = - ENOTTY ? LOG_DEBUG : LOG_WARNING ,
r ,
" Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m " ,
path , item - > attribute_value , item - > attribute_mask ) ;
2015-03-16 20:33:50 +01:00
return 0 ;
}
2012-09-03 23:13:18 +02:00
static int write_one_file ( Item * i , const char * path ) {
2014-06-18 00:01:39 +02:00
_cleanup_close_ int fd = - 1 ;
int flags , r = 0 ;
2012-09-03 23:13:18 +02:00
struct stat st ;
2014-06-10 22:48:56 +02:00
assert ( i ) ;
assert ( path ) ;
2014-06-18 00:01:39 +02:00
flags = i - > type = = CREATE_FILE ? O_CREAT | O_APPEND | O_NOFOLLOW :
i - > type = = TRUNCATE_FILE ? O_CREAT | O_TRUNC | O_NOFOLLOW : 0 ;
2012-09-03 23:13:18 +02:00
2014-06-18 00:01:39 +02:00
RUN_WITH_UMASK ( 0000 ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( path , S_IFREG ) ;
2014-06-18 00:01:39 +02:00
fd = open ( path , flags | O_NDELAY | O_CLOEXEC | O_WRONLY | O_NOCTTY , i - > mode ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2013-04-04 03:39:39 +02:00
}
2012-09-03 23:13:18 +02:00
if ( fd < 0 ) {
2015-01-24 07:54:05 +01:00
if ( i - > type = = WRITE_FILE & & errno = = ENOENT ) {
log_debug_errno ( errno , " Not writing \" %s \" : %m " , path ) ;
2012-09-03 23:13:18 +02:00
return 0 ;
2015-01-24 07:54:05 +01:00
}
2012-09-03 23:13:18 +02:00
2015-04-30 20:50:38 +02:00
r = - errno ;
if ( ! i - > argument & & errno = = EROFS & & stat ( path , & st ) = = 0 & &
( i - > type = = CREATE_FILE | | st . st_size = = 0 ) )
goto check_mode ;
return log_error_errno ( r , " Failed to create file %s: %m " , path ) ;
2012-09-03 23:13:18 +02:00
}
if ( i - > argument ) {
2015-04-10 16:03:24 +02:00
_cleanup_free_ char * unescaped = NULL , * replaced = NULL ;
2015-01-24 07:54:05 +01:00
2015-03-23 12:55:36 +01:00
log_debug ( " %s to \" %s \" . " , i - > type = = CREATE_FILE ? " Appending " : " Writing " , path ) ;
2012-09-03 23:13:18 +02:00
2015-04-06 20:11:41 +02:00
r = cunescape ( i - > argument , 0 , & unescaped ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to unescape parameter to write: %s " , i - > argument ) ;
2012-09-03 23:13:18 +02:00
2015-04-10 16:03:24 +02:00
r = specifier_printf ( unescaped , specifier_table , NULL , & replaced ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to replace specifiers in parameter to write '%s': %m " , unescaped ) ;
r = loop_write ( fd , replaced , strlen ( replaced ) , false ) ;
2015-01-24 07:54:05 +01:00
if ( r < 0 )
return log_error_errno ( r , " Failed to write file \" %s \" : %m " , path ) ;
} else
log_debug ( " \" %s \" has been created. " , path ) ;
2012-09-03 23:13:18 +02:00
2014-06-18 00:01:39 +02:00
fd = safe_close ( fd ) ;
2012-09-12 22:21:00 +02:00
2014-11-28 19:57:32 +01:00
if ( stat ( path , & st ) < 0 )
return log_error_errno ( errno , " stat(%s) failed : % m " , path) ;
2012-09-03 23:13:18 +02:00
2015-04-30 20:50:38 +02:00
check_mode :
2012-09-03 23:13:18 +02:00
if ( ! S_ISREG ( st . st_mode ) ) {
log_error ( " %s is not a file. " , path ) ;
return - EEXIST ;
}
2015-01-18 08:10:00 +01:00
r = path_set_perms ( i , path ) ;
2012-09-03 23:13:18 +02:00
if ( r < 0 )
return r ;
return 0 ;
}
2015-01-18 07:33:39 +01:00
typedef int ( * action_t ) ( Item * , const char * ) ;
static int item_do_children ( Item * i , const char * path , action_t action ) {
2013-04-18 09:11:22 +02:00
_cleanup_closedir_ DIR * d ;
2014-06-10 23:42:16 +02:00
int r = 0 ;
assert ( i ) ;
assert ( path ) ;
2011-12-15 23:11:07 +01:00
/* This returns the first error we run into, but nevertheless
* tries to go on */
2014-01-02 06:02:31 +01:00
d = opendir_nomod ( path ) ;
if ( ! d )
2014-06-10 23:42:16 +02:00
return errno = = ENOENT | | errno = = ENOTDIR ? 0 : - errno ;
2011-12-15 23:11:07 +01:00
for ( ; ; ) {
2014-06-10 23:42:16 +02:00
_cleanup_free_ char * p = NULL ;
2012-09-19 22:21:09 +02:00
struct dirent * de ;
2014-06-10 23:42:16 +02:00
int q ;
2011-12-15 23:11:07 +01:00
2013-12-19 12:26:07 +01:00
errno = 0 ;
de = readdir ( d ) ;
2014-06-10 23:42:16 +02:00
if ( ! de ) {
if ( errno ! = 0 & & r = = 0 )
r = - errno ;
2011-12-15 23:11:07 +01:00
break ;
2014-06-10 23:42:16 +02:00
}
2011-12-15 23:11:07 +01:00
2015-01-23 05:35:34 +01:00
if ( STR_IN_SET ( de - > d_name , " . " , " .. " ) )
2011-12-15 23:11:07 +01:00
continue ;
2014-06-10 23:42:16 +02:00
p = strjoin ( path , " / " , de - > d_name , NULL ) ;
if ( ! p )
return - ENOMEM ;
2011-12-15 23:11:07 +01:00
2015-01-18 07:33:39 +01:00
q = action ( i , p ) ;
2014-06-10 23:42:16 +02:00
if ( q < 0 & & q ! = - ENOENT & & r = = 0 )
r = q ;
2011-12-15 23:11:07 +01:00
2014-06-10 23:42:16 +02:00
if ( IN_SET ( de - > d_type , DT_UNKNOWN , DT_DIR ) ) {
2015-01-18 07:33:39 +01:00
q = item_do_children ( i , p , action ) ;
2014-06-10 23:42:16 +02:00
if ( q < 0 & & r = = 0 )
r = q ;
2011-12-15 23:11:07 +01:00
}
}
2014-06-10 23:42:16 +02:00
return r ;
2011-12-15 23:11:07 +01:00
}
2015-01-18 07:33:39 +01:00
static int glob_item ( Item * i , action_t action , bool recursive ) {
2014-01-02 06:02:31 +01:00
_cleanup_globfree_ glob_t g = {
2015-01-26 16:39:03 +01:00
. gl_closedir = ( void ( * ) ( void * ) ) closedir ,
. gl_readdir = ( struct dirent * ( * ) ( void * ) ) readdir ,
. gl_opendir = ( void * ( * ) ( const char * ) ) opendir_nomod ,
2014-01-02 06:02:31 +01:00
. gl_lstat = lstat ,
. gl_stat = stat ,
} ;
2014-06-10 23:42:16 +02:00
int r = 0 , k ;
2011-12-15 23:45:26 +01:00
char * * fn ;
errno = 0 ;
2014-01-02 06:02:31 +01:00
k = glob ( i - > path , GLOB_NOSORT | GLOB_BRACE | GLOB_ALTDIRFUNC , NULL , & g ) ;
2015-01-18 07:33:39 +01:00
if ( k ! = 0 & & k ! = GLOB_NOMATCH )
return log_error_errno ( errno ? : EIO , " glob(%s) failed : % m " , i->path) ;
2011-12-15 23:45:26 +01:00
2013-03-25 00:09:19 +01:00
STRV_FOREACH ( fn , g . gl_pathv ) {
k = action ( i , * fn ) ;
2014-06-10 23:42:16 +02:00
if ( k < 0 & & r = = 0 )
2011-12-15 23:45:26 +01:00
r = k ;
2015-01-18 07:33:39 +01:00
if ( recursive ) {
k = item_do_children ( i , * fn , action ) ;
if ( k < 0 & & r = = 0 )
r = k ;
}
2013-03-25 00:09:19 +01:00
}
2011-12-15 23:45:26 +01:00
return r ;
}
2015-02-01 18:29:27 +01:00
typedef enum {
CREATION_NORMAL ,
CREATION_EXISTING ,
CREATION_FORCE ,
2015-02-02 21:34:09 +01:00
_CREATION_MODE_MAX ,
_CREATION_MODE_INVALID = - 1
2015-02-01 18:29:27 +01:00
} CreationMode ;
2015-02-02 21:34:09 +01:00
static const char * creation_mode_verb_table [ _CREATION_MODE_MAX ] = {
[ CREATION_NORMAL ] = " Created " ,
[ CREATION_EXISTING ] = " Found existing " ,
[ CREATION_FORCE ] = " Created replacement " ,
} ;
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING ( creation_mode_verb , CreationMode ) ;
2015-02-01 18:29:27 +01:00
2010-10-18 22:38:41 +02:00
static int create_item ( Item * i ) {
2015-04-10 23:26:21 +02:00
_cleanup_free_ char * resolved = NULL ;
2010-10-18 22:38:41 +02:00
struct stat st ;
2013-10-21 15:24:18 +02:00
int r = 0 ;
2015-02-01 18:29:27 +01:00
CreationMode creation ;
2010-09-28 02:34:02 +02:00
2010-10-18 22:38:41 +02:00
assert ( i ) ;
2010-09-28 02:34:02 +02:00
2015-01-24 07:54:05 +01:00
log_debug ( " Running create action for entry %c %s " , ( char ) i - > type , i - > path ) ;
2010-10-18 22:38:41 +02:00
switch ( i - > type ) {
case IGNORE_PATH :
2013-01-18 16:13:08 +01:00
case IGNORE_DIRECTORY_PATH :
2010-10-18 22:38:41 +02:00
case REMOVE_PATH :
case RECURSIVE_REMOVE_PATH :
return 0 ;
2010-09-28 02:34:02 +02:00
2010-10-18 22:38:41 +02:00
case CREATE_FILE :
2012-01-18 16:39:04 +01:00
case TRUNCATE_FILE :
2012-09-28 02:48:13 +02:00
r = write_one_file ( i , i - > path ) ;
if ( r < 0 )
return r ;
break ;
2013-09-17 23:33:30 +02:00
2015-04-10 18:07:04 +02:00
case COPY_FILES : {
r = specifier_printf ( i - > argument , specifier_table , NULL , & resolved ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to substitute specifiers in copy source %s: %m " , i - > argument ) ;
log_debug ( " Copying tree \" %s \" to \" %s \" . " , resolved , i - > path ) ;
r = copy_tree ( resolved , i - > path , false ) ;
2015-04-30 20:50:38 +02:00
if ( r = = - EROFS & & stat ( i - > path , & st ) = = 0 )
r = - EEXIST ;
2014-06-10 23:02:40 +02:00
if ( r < 0 ) {
2014-06-19 19:36:08 +02:00
struct stat a , b ;
2014-11-28 19:13:53 +01:00
if ( r ! = - EEXIST )
return log_error_errno ( r , " Failed to copy files to %s: %m " , i - > path ) ;
2014-06-19 19:36:08 +02:00
2015-04-10 18:07:04 +02:00
if ( stat ( resolved , & a ) < 0 )
return log_error_errno ( errno , " stat(%s) failed : % m " , resolved) ;
2014-06-19 19:36:08 +02:00
2014-11-28 19:57:32 +01:00
if ( stat ( i - > path , & b ) < 0 )
return log_error_errno ( errno , " stat(%s) failed : % m " , i->path) ;
2014-06-19 19:36:08 +02:00
if ( ( a . st_mode ^ b . st_mode ) & S_IFMT ) {
log_debug ( " Can't copy to %s, file exists already and is of different type " , i - > path ) ;
return 0 ;
}
2014-06-10 23:02:40 +02:00
}
2015-01-18 08:10:00 +01:00
r = path_set_perms ( i , i - > path ) ;
2014-06-10 23:02:40 +02:00
if ( r < 0 )
return r ;
break ;
2012-09-03 23:13:18 +02:00
case WRITE_FILE :
2015-01-18 07:33:39 +01:00
r = glob_item ( i , write_one_file , false ) ;
2011-12-15 23:44:23 +01:00
if ( r < 0 )
return r ;
2010-09-28 02:34:02 +02:00
2010-10-18 22:38:41 +02:00
break ;
case CREATE_DIRECTORY :
2014-12-27 18:46:36 +01:00
case TRUNCATE_DIRECTORY :
case CREATE_SUBVOLUME :
2015-10-21 19:46:23 +02:00
case CREATE_SUBVOLUME_INHERIT_QUOTA :
case CREATE_SUBVOLUME_NEW_QUOTA :
2010-09-28 02:34:02 +02:00
2014-12-27 18:46:36 +01:00
RUN_WITH_UMASK ( 0000 )
2013-04-04 03:39:39 +02:00
mkdir_parents_label ( i - > path , 0755 ) ;
2014-12-27 18:46:36 +01:00
2015-10-21 19:46:23 +02:00
if ( IN_SET ( i - > type , CREATE_SUBVOLUME , CREATE_SUBVOLUME_INHERIT_QUOTA , CREATE_SUBVOLUME_NEW_QUOTA ) ) {
RUN_WITH_UMASK ( ( ~ i - > mode ) & 0777 )
2014-12-27 18:46:36 +01:00
r = btrfs_subvol_make ( i - > path ) ;
2015-10-21 19:46:23 +02:00
} else
2014-12-27 18:46:36 +01:00
r = 0 ;
2015-01-24 07:54:05 +01:00
if ( IN_SET ( i - > type , CREATE_DIRECTORY , TRUNCATE_DIRECTORY ) | | r = = - ENOTTY )
2014-12-27 18:46:36 +01:00
RUN_WITH_UMASK ( 0000 )
r = mkdir_label ( i - > path , i - > mode ) ;
2010-09-28 02:34:02 +02:00
2014-06-19 19:36:08 +02:00
if ( r < 0 ) {
2015-05-15 21:47:22 +02:00
int k ;
2010-09-28 02:34:02 +02:00
2015-05-15 21:47:22 +02:00
if ( r ! = - EEXIST & & r ! = - EROFS )
return log_error_errno ( r , " Failed to create directory or subvolume \" %s \" : %m " , i - > path ) ;
2010-09-28 02:34:02 +02:00
2015-05-15 21:47:22 +02:00
k = is_dir ( i - > path , false ) ;
if ( k = = - ENOENT & & r = = - EROFS )
return log_error_errno ( r , " %s does not exist and cannot be created as the file system is read-only. " , i - > path ) ;
if ( k < 0 )
return log_error_errno ( k , " Failed to check if %s exists: %m " , i - > path ) ;
if ( ! k ) {
log_warning ( " \" %s \" already exists and is not a directory. " , i - > path ) ;
2014-06-19 19:36:08 +02:00
return 0 ;
}
2015-02-01 18:29:27 +01:00
creation = CREATION_EXISTING ;
} else
creation = CREATION_NORMAL ;
2015-05-15 21:47:22 +02:00
2015-02-02 21:34:09 +01:00
log_debug ( " %s directory \" %s \" . " , creation_mode_verb_to_string ( creation ) , i - > path ) ;
2010-09-28 02:34:02 +02:00
2015-10-21 19:46:23 +02:00
if ( IN_SET ( i - > type , CREATE_SUBVOLUME_NEW_QUOTA , CREATE_SUBVOLUME_INHERIT_QUOTA ) ) {
r = btrfs_subvol_auto_qgroup ( i - > path , 0 , i - > type = = CREATE_SUBVOLUME_NEW_QUOTA ) ;
if ( r = = - ENOTTY ) {
log_debug_errno ( r , " Couldn't adjust quota for subvolume \" %s \" because of unsupported file system or because directory is not a subvolume: %m " , i - > path ) ;
return 0 ;
}
if ( r = = - EROFS ) {
log_debug_errno ( r , " Couldn't adjust quota for subvolume \" %s \" because of read-only file system: %m " , i - > path ) ;
return 0 ;
}
if ( r < 0 )
return log_error_errno ( r , " Failed to adjust quota for subvolume \" %s \" : %m " , i - > path ) ;
if ( r > 0 )
log_debug ( " Adjusted quota for subvolume \" %s \" . " , i - > path ) ;
if ( r = = 0 )
log_debug ( " Quota for subvolume \" %s \" already in place, no change made. " , i - > path ) ;
}
2015-01-18 08:10:00 +01:00
r = path_set_perms ( i , i - > path ) ;
2011-12-15 23:44:23 +01:00
if ( r < 0 )
return r ;
2010-10-18 22:38:41 +02:00
break ;
2011-07-12 03:56:56 +02:00
case CREATE_FIFO :
2013-04-04 03:39:39 +02:00
RUN_WITH_UMASK ( 0000 ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , S_IFIFO ) ;
2013-04-04 03:39:39 +02:00
r = mkfifo ( i - > path , i - > mode ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2013-04-04 03:39:39 +02:00
}
2011-07-12 03:56:56 +02:00
2014-06-17 23:50:22 +02:00
if ( r < 0 ) {
2014-11-28 19:57:32 +01:00
if ( errno ! = EEXIST )
return log_error_errno ( errno , " Failed to create fifo %s: %m " , i - > path ) ;
2011-07-12 03:56:56 +02:00
2015-05-15 21:48:20 +02:00
if ( lstat ( i - > path , & st ) < 0 )
2014-11-28 19:57:32 +01:00
return log_error_errno ( errno , " stat(%s) failed : % m " , i->path) ;
2011-07-12 03:56:56 +02:00
2014-06-17 23:50:22 +02:00
if ( ! S_ISFIFO ( st . st_mode ) ) {
if ( i - > force ) {
RUN_WITH_UMASK ( 0000 ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , S_IFIFO ) ;
2014-06-17 23:50:22 +02:00
r = mkfifo_atomic ( i - > path , i - > mode ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2014-06-17 23:50:22 +02:00
}
2014-11-28 18:50:43 +01:00
if ( r < 0 )
return log_error_errno ( r , " Failed to create fifo %s: %m " , i - > path ) ;
2015-02-01 18:29:27 +01:00
creation = CREATION_FORCE ;
2014-06-17 23:50:22 +02:00
} else {
2015-05-15 21:48:20 +02:00
log_warning ( " \" %s \" already exists and is not a fifo. " , i - > path ) ;
2014-06-17 23:50:22 +02:00
return 0 ;
}
2015-02-01 18:29:27 +01:00
} else
creation = CREATION_EXISTING ;
} else
creation = CREATION_NORMAL ;
2015-02-02 21:34:09 +01:00
log_debug ( " %s fifo \" %s \" . " , creation_mode_verb_to_string ( creation ) , i - > path ) ;
2011-07-12 03:56:56 +02:00
2015-01-18 08:10:00 +01:00
r = path_set_perms ( i , i - > path ) ;
2011-12-15 23:44:23 +01:00
if ( r < 0 )
return r ;
2011-07-12 03:56:56 +02:00
break ;
2015-04-10 18:07:04 +02:00
}
2011-12-15 23:11:07 +01:00
2015-04-10 18:07:04 +02:00
case CREATE_SYMLINK : {
r = specifier_printf ( i - > argument , specifier_table , NULL , & resolved ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to substitute specifiers in symlink target %s: %m " , i - > argument ) ;
2012-01-17 15:04:12 +01:00
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , S_IFLNK ) ;
2015-04-10 18:07:04 +02:00
r = symlink ( resolved , i - > path ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2012-04-17 16:05:03 +02:00
2012-01-17 15:04:12 +01:00
if ( r < 0 ) {
2014-06-16 13:21:07 +02:00
_cleanup_free_ char * x = NULL ;
2012-01-17 15:04:12 +01:00
2014-11-28 19:57:32 +01:00
if ( errno ! = EEXIST )
2015-04-10 18:07:04 +02:00
return log_error_errno ( errno , " symlink(%s, %s) failed : % m " , resolved, i->path) ;
2014-06-16 13:21:07 +02:00
r = readlink_malloc ( i - > path , & x ) ;
2015-04-10 18:07:04 +02:00
if ( r < 0 | | ! streq ( resolved , x ) ) {
2014-06-16 13:21:07 +02:00
if ( i - > force ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , S_IFLNK ) ;
2015-04-10 18:07:04 +02:00
r = symlink_atomic ( resolved , i - > path ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2014-06-16 13:21:07 +02:00
2014-11-28 18:50:43 +01:00
if ( r < 0 )
2015-04-10 18:07:04 +02:00
return log_error_errno ( r , " symlink(%s, %s) failed : % m " , resolved, i->path) ;
2015-05-15 21:48:20 +02:00
2015-02-01 18:29:27 +01:00
creation = CREATION_FORCE ;
2014-06-17 23:50:22 +02:00
} else {
2015-01-24 07:54:05 +01:00
log_debug ( " \" %s \" is not a symlink or does not point to the correct path. " , i - > path ) ;
2014-06-17 23:50:22 +02:00
return 0 ;
}
2015-02-01 18:29:27 +01:00
} else
creation = CREATION_EXISTING ;
} else
2015-05-15 21:48:20 +02:00
2015-02-01 18:29:27 +01:00
creation = CREATION_NORMAL ;
2015-02-02 21:34:09 +01:00
log_debug ( " %s symlink \" %s \" . " , creation_mode_verb_to_string ( creation ) , i - > path ) ;
2012-01-17 15:04:12 +01:00
break ;
2015-04-10 18:07:04 +02:00
}
2012-01-17 15:04:12 +01:00
case CREATE_BLOCK_DEVICE :
case CREATE_CHAR_DEVICE : {
2012-09-06 08:39:55 +02:00
mode_t file_type ;
if ( have_effective_cap ( CAP_MKNOD ) = = 0 ) {
/* In a container we lack CAP_MKNOD. We
2013-04-15 04:37:54 +02:00
shouldn ' t attempt to create the device node in
2012-09-06 08:39:55 +02:00
that case to avoid noise , and we don ' t support
virtualized devices in containers anyway . */
log_debug ( " We lack CAP_MKNOD, skipping creation of device node %s. " , i - > path ) ;
return 0 ;
}
2014-06-17 23:50:22 +02:00
file_type = i - > type = = CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR ;
2012-01-17 15:04:12 +01:00
2013-04-04 03:39:39 +02:00
RUN_WITH_UMASK ( 0000 ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , file_type ) ;
2013-04-04 03:39:39 +02:00
r = mknod ( i - > path , i - > mode | file_type , i - > major_minor ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2013-04-04 03:39:39 +02:00
}
2012-01-17 15:04:12 +01:00
2014-06-13 04:11:11 +02:00
if ( r < 0 ) {
if ( errno = = EPERM ) {
log_debug ( " We lack permissions, possibly because of cgroup configuration; "
" skipping creation of device node %s. " , i - > path ) ;
return 0 ;
}
2014-11-28 19:57:32 +01:00
if ( errno ! = EEXIST )
return log_error_errno ( errno , " Failed to create device node %s: %m " , i - > path ) ;
2012-01-17 15:04:12 +01:00
2015-05-15 21:48:20 +02:00
if ( lstat ( i - > path , & st ) < 0 )
2014-11-28 19:57:32 +01:00
return log_error_errno ( errno , " stat(%s) failed : % m " , i->path) ;
2012-01-17 15:04:12 +01:00
2014-06-17 23:50:22 +02:00
if ( ( st . st_mode & S_IFMT ) ! = file_type ) {
if ( i - > force ) {
RUN_WITH_UMASK ( 0000 ) {
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_prepare ( i - > path , file_type ) ;
2014-06-17 23:50:22 +02:00
r = mknod_atomic ( i - > path , i - > mode | file_type , i - > major_minor ) ;
2014-10-23 19:41:27 +02:00
mac_selinux_create_file_clear ( ) ;
2014-06-17 23:50:22 +02:00
}
2014-11-28 18:50:43 +01:00
if ( r < 0 )
2015-02-01 18:29:27 +01:00
return log_error_errno ( r , " Failed to create device node \" %s \" : %m " , i - > path ) ;
creation = CREATION_FORCE ;
2014-06-17 23:50:22 +02:00
} else {
log_debug ( " %s is not a device node. " , i - > path ) ;
return 0 ;
}
2015-02-01 18:29:27 +01:00
} else
creation = CREATION_EXISTING ;
} else
creation = CREATION_NORMAL ;
2015-05-15 21:48:20 +02:00
2015-02-01 18:29:27 +01:00
log_debug ( " %s %s device node \" %s \" %u:%u. " ,
2015-02-02 21:34:09 +01:00
creation_mode_verb_to_string ( creation ) ,
2015-01-24 07:54:05 +01:00
i - > type = = CREATE_BLOCK_DEVICE ? " block " : " char " ,
i - > path , major ( i - > mode ) , minor ( i - > mode ) ) ;
2012-01-17 15:04:12 +01:00
2015-01-18 08:10:00 +01:00
r = path_set_perms ( i , i - > path ) ;
2012-01-17 15:04:12 +01:00
if ( r < 0 )
return r ;
break ;
}
2014-06-10 23:42:16 +02:00
case ADJUST_MODE :
2011-12-16 18:27:35 +01:00
case RELABEL_PATH :
2015-01-18 08:10:00 +01:00
r = glob_item ( i , path_set_perms , false ) ;
2011-12-16 18:27:35 +01:00
if ( r < 0 )
2013-06-21 15:57:42 +02:00
return r ;
2011-12-16 18:27:35 +01:00
break ;
2011-12-15 23:11:07 +01:00
case RECURSIVE_RELABEL_PATH :
2015-01-18 08:10:00 +01:00
r = glob_item ( i , path_set_perms , true ) ;
2011-12-15 23:11:07 +01:00
if ( r < 0 )
return r ;
2014-12-04 10:32:10 +01:00
break ;
2014-06-10 23:42:16 +02:00
2014-12-04 10:32:10 +01:00
case SET_XATTR :
2015-01-18 08:10:00 +01:00
r = glob_item ( i , path_set_xattrs , false ) ;
if ( r < 0 )
return r ;
break ;
case RECURSIVE_SET_XATTR :
r = glob_item ( i , path_set_xattrs , true ) ;
2014-12-04 10:32:10 +01:00
if ( r < 0 )
return r ;
2014-06-10 23:42:16 +02:00
break ;
2015-01-18 05:27:39 +01:00
case SET_ACL :
2015-01-18 08:10:00 +01:00
r = glob_item ( i , path_set_acls , false ) ;
2015-01-18 05:27:39 +01:00
if ( r < 0 )
return r ;
2015-01-18 08:10:00 +01:00
break ;
case RECURSIVE_SET_ACL :
r = glob_item ( i , path_set_acls , true ) ;
if ( r < 0 )
return r ;
break ;
2015-03-16 20:33:50 +01:00
2015-04-08 22:35:52 +02:00
case SET_ATTRIBUTE :
r = glob_item ( i , path_set_attribute , false ) ;
2015-03-16 20:33:50 +01:00
if ( r < 0 )
return r ;
break ;
2015-04-08 22:35:52 +02:00
case RECURSIVE_SET_ATTRIBUTE :
r = glob_item ( i , path_set_attribute , true ) ;
2015-03-16 20:33:50 +01:00
if ( r < 0 )
return r ;
break ;
2010-10-18 22:38:41 +02:00
}
2011-12-15 23:44:23 +01:00
return 0 ;
2010-10-18 22:38:41 +02:00
}
2011-12-15 21:32:50 +01:00
static int remove_item_instance ( Item * i , const char * instance ) {
2010-10-18 22:38:41 +02:00
int r ;
assert ( i ) ;
switch ( i - > type ) {
case REMOVE_PATH :
2014-11-28 19:57:32 +01:00
if ( remove ( instance ) < 0 & & errno ! = ENOENT )
2015-01-09 08:00:37 +01:00
return log_error_errno ( errno , " rm(%s) : % m " , instance) ;
2010-10-18 22:38:41 +02:00
break ;
case TRUNCATE_DIRECTORY :
case RECURSIVE_REMOVE_PATH :
2012-06-20 14:31:00 +02:00
/* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf ( ) so that ' x ' is honoured . */
2015-01-24 07:54:05 +01:00
log_debug ( " rm -rf \" %s \" " , instance ) ;
2015-06-15 19:11:15 +02:00
r = rm_rf ( instance , ( i - > type = = RECURSIVE_REMOVE_PATH ? REMOVE_ROOT | REMOVE_SUBVOLUME : 0 ) | REMOVE_PHYSICAL ) ;
2014-11-28 18:50:43 +01:00
if ( r < 0 & & r ! = - ENOENT )
return log_error_errno ( r , " rm_rf(%s) : % m " , instance) ;
2010-10-18 22:38:41 +02:00
break ;
2015-01-23 05:35:34 +01:00
default :
assert_not_reached ( " wut? " ) ;
2010-10-18 22:38:41 +02:00
}
return 0 ;
}
2011-12-15 21:32:50 +01:00
static int remove_item ( Item * i ) {
2011-12-15 23:45:26 +01:00
int r = 0 ;
2011-02-12 09:31:25 +01:00
assert ( i ) ;
2015-01-24 07:54:05 +01:00
log_debug ( " Running remove action for entry %c %s " , ( char ) i - > type , i - > path ) ;
2011-02-12 09:31:25 +01:00
switch ( i - > type ) {
case CREATE_FILE :
case TRUNCATE_FILE :
case CREATE_DIRECTORY :
2014-12-27 18:46:36 +01:00
case CREATE_SUBVOLUME :
2015-10-21 19:46:23 +02:00
case CREATE_SUBVOLUME_INHERIT_QUOTA :
case CREATE_SUBVOLUME_NEW_QUOTA :
2011-07-12 03:56:56 +02:00
case CREATE_FIFO :
2012-01-17 15:04:12 +01:00
case CREATE_SYMLINK :
case CREATE_CHAR_DEVICE :
case CREATE_BLOCK_DEVICE :
2011-02-12 09:31:25 +01:00
case IGNORE_PATH :
2013-01-18 16:13:08 +01:00
case IGNORE_DIRECTORY_PATH :
2014-06-10 23:42:16 +02:00
case ADJUST_MODE :
2011-12-16 18:27:35 +01:00
case RELABEL_PATH :
2011-12-15 23:11:07 +01:00
case RECURSIVE_RELABEL_PATH :
2012-01-18 16:39:04 +01:00
case WRITE_FILE :
2014-06-10 23:02:40 +02:00
case COPY_FILES :
2014-12-04 10:32:10 +01:00
case SET_XATTR :
2015-01-18 08:10:00 +01:00
case RECURSIVE_SET_XATTR :
2015-01-18 05:27:39 +01:00
case SET_ACL :
2015-01-18 08:10:00 +01:00
case RECURSIVE_SET_ACL :
2015-04-08 22:35:52 +02:00
case SET_ATTRIBUTE :
case RECURSIVE_SET_ATTRIBUTE :
2011-02-12 09:31:25 +01:00
break ;
case REMOVE_PATH :
case TRUNCATE_DIRECTORY :
2011-12-15 23:45:26 +01:00
case RECURSIVE_REMOVE_PATH :
2015-01-18 07:33:39 +01:00
r = glob_item ( i , remove_item_instance , false ) ;
2011-12-15 23:45:26 +01:00
break ;
2011-02-12 09:31:25 +01:00
}
2011-12-15 23:45:26 +01:00
return r ;
2011-02-12 09:31:25 +01:00
}
2013-01-18 16:13:08 +01:00
static int clean_item_instance ( Item * i , const char * instance ) {
2013-04-18 09:11:22 +02:00
_cleanup_closedir_ DIR * d = NULL ;
2013-01-18 16:13:08 +01:00
struct stat s , ps ;
bool mountpoint ;
usec_t cutoff , n ;
2015-01-24 07:54:05 +01:00
char timestamp [ FORMAT_TIMESTAMP_MAX ] ;
2013-01-18 16:13:08 +01:00
assert ( i ) ;
if ( ! i - > age_set )
return 0 ;
n = now ( CLOCK_REALTIME ) ;
if ( n < i - > age )
return 0 ;
cutoff = n - i - > age ;
2014-01-02 06:02:31 +01:00
d = opendir_nomod ( instance ) ;
2013-01-18 16:13:08 +01:00
if ( ! d ) {
2015-01-24 07:54:05 +01:00
if ( errno = = ENOENT | | errno = = ENOTDIR ) {
log_debug_errno ( errno , " Directory \" %s \" : %m " , instance ) ;
2013-01-18 16:13:08 +01:00
return 0 ;
2015-01-24 07:54:05 +01:00
}
2013-01-18 16:13:08 +01:00
2015-01-24 07:54:05 +01:00
log_error_errno ( errno , " Failed to open directory %s: %m " , instance ) ;
2013-01-18 16:13:08 +01:00
return - errno ;
}
2014-11-28 19:57:32 +01:00
if ( fstat ( dirfd ( d ) , & s ) < 0 )
return log_error_errno ( errno , " stat(%s) failed : % m " , i->path) ;
2013-01-18 16:13:08 +01:00
if ( ! S_ISDIR ( s . st_mode ) ) {
log_error ( " %s is not a directory. " , i - > path ) ;
2013-03-04 00:42:52 +01:00
return - ENOTDIR ;
2013-01-18 16:13:08 +01:00
}
2014-11-28 19:57:32 +01:00
if ( fstatat ( dirfd ( d ) , " .. " , & ps , AT_SYMLINK_NOFOLLOW ) ! = 0 )
return log_error_errno ( errno , " stat(%s/..) failed : % m " , i->path) ;
2013-01-18 16:13:08 +01:00
2015-10-23 20:15:17 +02:00
mountpoint = s . st_dev ! = ps . st_dev | | s . st_ino = = ps . st_ino ;
2013-01-18 16:13:08 +01:00
2015-01-24 07:54:05 +01:00
log_debug ( " Cleanup threshold for %s \" %s \" is %s " ,
mountpoint ? " mount point " : " directory " ,
instance ,
format_timestamp_us ( timestamp , sizeof ( timestamp ) , cutoff ) ) ;
return dir_cleanup ( i , instance , d , & s , cutoff , s . st_dev , mountpoint ,
MAX_DEPTH , i - > keep_first_level ) ;
2013-01-18 16:13:08 +01:00
}
static int clean_item ( Item * i ) {
int r = 0 ;
assert ( i ) ;
2015-01-24 07:54:05 +01:00
log_debug ( " Running clean action for entry %c %s " , ( char ) i - > type , i - > path ) ;
2013-01-18 16:13:08 +01:00
switch ( i - > type ) {
case CREATE_DIRECTORY :
2014-12-27 18:46:36 +01:00
case CREATE_SUBVOLUME :
2015-10-21 19:46:23 +02:00
case CREATE_SUBVOLUME_INHERIT_QUOTA :
case CREATE_SUBVOLUME_NEW_QUOTA :
2013-01-18 16:13:08 +01:00
case TRUNCATE_DIRECTORY :
case IGNORE_PATH :
2014-06-10 23:02:40 +02:00
case COPY_FILES :
2013-01-18 16:13:08 +01:00
clean_item_instance ( i , i - > path ) ;
break ;
case IGNORE_DIRECTORY_PATH :
2015-01-18 07:33:39 +01:00
r = glob_item ( i , clean_item_instance , false ) ;
2013-01-18 16:13:08 +01:00
break ;
default :
break ;
}
return r ;
}
2015-01-09 08:00:37 +01:00
static int process_item_array ( ItemArray * array ) ;
2010-10-18 22:38:41 +02:00
static int process_item ( Item * i ) {
2015-01-06 20:33:46 +01:00
int r , q , p , t = 0 ;
2014-10-01 14:33:22 +02:00
_cleanup_free_ char * prefix = NULL ;
2010-10-18 22:38:41 +02:00
assert ( i ) ;
2014-06-11 01:37:35 +02:00
if ( i - > done )
return 0 ;
i - > done = true ;
2014-10-01 14:33:22 +02:00
prefix = malloc ( strlen ( i - > path ) + 1 ) ;
if ( ! prefix )
return log_oom ( ) ;
2014-06-11 01:37:35 +02:00
PATH_FOREACH_PREFIX ( prefix , i - > path ) {
2015-01-09 08:00:37 +01:00
ItemArray * j ;
2014-06-11 01:37:35 +02:00
2015-04-22 18:18:56 +02:00
j = ordered_hashmap_get ( items , prefix ) ;
2015-01-06 15:53:12 +01:00
if ( j ) {
int s ;
2015-01-09 08:00:37 +01:00
s = process_item_array ( j ) ;
2015-01-06 15:53:12 +01:00
if ( s < 0 & & t = = 0 )
t = s ;
}
2014-06-11 01:37:35 +02:00
}
2010-10-18 22:38:41 +02:00
r = arg_create ? create_item ( i ) : 0 ;
2011-12-15 21:32:50 +01:00
q = arg_remove ? remove_item ( i ) : 0 ;
2010-10-18 22:38:41 +02:00
p = arg_clean ? clean_item ( i ) : 0 ;
2015-01-06 15:53:12 +01:00
return t < 0 ? t :
r < 0 ? r :
q < 0 ? q :
p ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
static int process_item_array ( ItemArray * array ) {
unsigned n ;
int r = 0 , k ;
2014-06-12 23:07:17 +02:00
2015-01-09 08:00:37 +01:00
assert ( array ) ;
for ( n = 0 ; n < array - > count ; n + + ) {
k = process_item ( array - > items + n ) ;
if ( k < 0 & & r = = 0 )
r = k ;
}
return r ;
}
2010-10-18 22:38:41 +02:00
2015-01-09 08:00:37 +01:00
static void item_free_contents ( Item * i ) {
assert ( i ) ;
2010-10-18 22:38:41 +02:00
free ( i - > path ) ;
2012-01-17 15:04:12 +01:00
free ( i - > argument ) ;
2014-12-04 10:32:10 +01:00
strv_free ( i - > xattrs ) ;
2015-01-18 05:27:39 +01:00
# ifdef HAVE_ACL
acl_free ( i - > acl_access ) ;
acl_free ( i - > acl_default ) ;
# endif
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
static void item_array_free ( ItemArray * a ) {
unsigned n ;
if ( ! a )
return ;
for ( n = 0 ; n < a - > count ; n + + )
item_free_contents ( a - > items + n ) ;
free ( a - > items ) ;
free ( a ) ;
}
2013-07-19 15:43:12 +02:00
2015-04-10 16:22:22 +02:00
static int item_compare ( const void * a , const void * b ) {
const Item * x = a , * y = b ;
/* Make sure that the ownership taking item is put first, so
* that we first create the node , and then can adjust it */
if ( takes_ownership ( x - > type ) & & ! takes_ownership ( y - > type ) )
return - 1 ;
if ( ! takes_ownership ( x - > type ) & & takes_ownership ( y - > type ) )
return 1 ;
return ( int ) x - > type - ( int ) y - > type ;
}
2015-01-09 08:00:37 +01:00
static bool item_compatible ( Item * a , Item * b ) {
2011-04-08 04:49:43 +02:00
assert ( a ) ;
assert ( b ) ;
2015-01-09 08:00:37 +01:00
assert ( streq ( a - > path , b - > path ) ) ;
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
if ( takes_ownership ( a - > type ) & & takes_ownership ( b - > type ) )
/* check if the items are the same */
return streq_ptr ( a - > argument , b - > argument ) & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > uid_set = = b - > uid_set & &
a - > uid = = b - > uid & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > gid_set = = b - > gid_set & &
a - > gid = = b - > gid & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > mode_set = = b - > mode_set & &
a - > mode = = b - > mode & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > age_set = = b - > age_set & &
a - > age = = b - > age & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > mask_perms = = b - > mask_perms & &
2011-04-08 04:49:43 +02:00
2015-01-09 08:00:37 +01:00
a - > keep_first_level = = b - > keep_first_level & &
2012-01-17 15:04:12 +01:00
2015-01-09 08:00:37 +01:00
a - > major_minor = = b - > major_minor ;
2012-01-17 15:04:12 +01:00
2011-04-08 04:49:43 +02:00
return true ;
}
2013-07-24 17:10:05 +02:00
static bool should_include_path ( const char * path ) {
char * * prefix ;
2014-06-11 10:14:07 +02:00
STRV_FOREACH ( prefix , arg_exclude_prefixes )
2015-01-24 07:54:05 +01:00
if ( path_startswith ( path , * prefix ) ) {
log_debug ( " Entry \" %s \" matches exclude prefix \" %s \" , skipping. " ,
path , * prefix ) ;
2013-07-24 17:19:24 +02:00
return false ;
2015-01-24 07:54:05 +01:00
}
2013-07-24 17:10:05 +02:00
2014-06-11 10:14:07 +02:00
STRV_FOREACH ( prefix , arg_include_prefixes )
2015-01-24 07:54:05 +01:00
if ( path_startswith ( path , * prefix ) ) {
log_debug ( " Entry \" %s \" matches include prefix \" %s \" . " , path , * prefix ) ;
2013-07-24 17:10:05 +02:00
return true ;
2015-01-24 07:54:05 +01:00
}
2013-07-24 17:10:05 +02:00
2013-07-24 17:19:24 +02:00
/* no matches, so we should include this path only if we
* have no whitelist at all */
2015-01-24 07:54:05 +01:00
if ( strv_length ( arg_include_prefixes ) = = 0 )
return true ;
log_debug ( " Entry \" %s \" does not match any include prefix, skipping. " , path ) ;
return false ;
2013-07-24 17:10:05 +02:00
}
2011-02-13 14:00:54 +01:00
static int parse_line ( const char * fname , unsigned line , const char * buffer ) {
2013-09-17 18:02:02 +02:00
2014-06-10 22:50:46 +02:00
_cleanup_free_ char * action = NULL , * mode = NULL , * user = NULL , * group = NULL , * age = NULL , * path = NULL ;
2015-01-09 08:00:37 +01:00
_cleanup_ ( item_free_contents ) Item i = { } ;
ItemArray * existing ;
2015-04-22 18:18:56 +02:00
OrderedHashmap * h ;
2015-03-09 20:11:44 +01:00
int r , pos ;
2015-01-09 07:11:01 +01:00
bool force = false , boot = false ;
2010-10-18 22:38:41 +02:00
assert ( fname ) ;
assert ( line > = 1 ) ;
assert ( buffer ) ;
2015-06-23 18:20:53 +02:00
r = extract_many_words (
2015-03-23 12:55:36 +01:00
& buffer ,
2015-06-23 18:20:53 +02:00
NULL ,
2015-06-23 18:26:49 +02:00
EXTRACT_QUOTES ,
2015-03-23 12:55:36 +01:00
& action ,
& path ,
& mode ,
& user ,
& group ,
& age ,
NULL ) ;
2015-03-09 20:11:44 +01:00
if ( r < 0 )
return log_error_errno ( r , " [%s:%u] Failed to parse line: %m " , fname , line ) ;
else if ( r < 2 ) {
2010-10-18 22:38:41 +02:00
log_error ( " [%s:%u] Syntax error. " , fname , line ) ;
2013-03-31 22:29:46 +02:00
return - EIO ;
2010-09-28 02:34:02 +02:00
}
2015-04-21 01:10:19 +02:00
if ( ! isempty ( buffer ) & & ! streq ( buffer , " - " ) ) {
2015-03-23 12:55:36 +01:00
i . argument = strdup ( buffer ) ;
if ( ! i . argument )
return log_oom ( ) ;
}
2014-06-16 13:21:07 +02:00
if ( isempty ( action ) ) {
log_error ( " [%s:%u] Command too short '%s'. " , fname , line , action ) ;
2013-12-21 02:25:39 +01:00
return - EINVAL ;
2014-06-16 13:21:07 +02:00
}
2015-01-09 07:11:01 +01:00
for ( pos = 1 ; action [ pos ] ; pos + + ) {
if ( action [ pos ] = = ' ! ' & & ! boot )
boot = true ;
else if ( action [ pos ] = = ' + ' & & ! force )
force = true ;
else {
log_error ( " [%s:%u] Unknown modifiers in command '%s' " ,
fname , line , action ) ;
return - EINVAL ;
}
2014-06-16 13:21:07 +02:00
}
2015-01-24 07:54:05 +01:00
if ( boot & & ! arg_boot ) {
log_debug ( " Ignoring entry %s \" %s \" because --boot is not specified. " ,
action , path ) ;
2013-12-21 02:25:39 +01:00
return 0 ;
2015-01-24 07:54:05 +01:00
}
2013-12-21 02:25:39 +01:00
2015-01-09 08:00:37 +01:00
i . type = action [ 0 ] ;
i . force = force ;
2014-06-16 13:21:07 +02:00
2015-01-09 08:00:37 +01:00
r = specifier_printf ( path , specifier_table , NULL , & i . path ) ;
2013-09-17 18:02:02 +02:00
if ( r < 0 ) {
log_error ( " [%s:%u] Failed to replace specifiers: %s " , fname , line , path ) ;
return r ;
}
2015-01-09 08:00:37 +01:00
switch ( i . type ) {
2012-01-17 15:04:12 +01:00
2011-12-16 18:27:35 +01:00
case CREATE_DIRECTORY :
2014-12-27 18:46:36 +01:00
case CREATE_SUBVOLUME :
2015-10-21 19:46:23 +02:00
case CREATE_SUBVOLUME_INHERIT_QUOTA :
case CREATE_SUBVOLUME_NEW_QUOTA :
2011-12-16 18:27:35 +01:00
case TRUNCATE_DIRECTORY :
case CREATE_FIFO :
case IGNORE_PATH :
2013-01-18 16:13:08 +01:00
case IGNORE_DIRECTORY_PATH :
2011-12-16 18:27:35 +01:00
case REMOVE_PATH :
case RECURSIVE_REMOVE_PATH :
2014-06-10 23:42:16 +02:00
case ADJUST_MODE :
2011-12-16 18:27:35 +01:00
case RELABEL_PATH :
case RECURSIVE_RELABEL_PATH :
2015-04-10 14:46:05 +02:00
if ( i . argument )
2015-04-10 16:03:24 +02:00
log_warning ( " [%s:%u] %c lines don't take argument fields, ignoring. " , fname , line , i . type ) ;
2015-04-10 14:46:05 +02:00
break ;
case CREATE_FILE :
case TRUNCATE_FILE :
2011-12-16 18:27:35 +01:00
break ;
2012-01-17 15:04:12 +01:00
case CREATE_SYMLINK :
2015-01-09 08:00:37 +01:00
if ( ! i . argument ) {
i . argument = strappend ( " /usr/share/factory/ " , i . path ) ;
if ( ! i . argument )
2014-06-20 15:57:43 +02:00
return log_oom ( ) ;
2012-01-17 15:04:12 +01:00
}
break ;
2012-01-18 16:39:04 +01:00
case WRITE_FILE :
2015-01-09 08:00:37 +01:00
if ( ! i . argument ) {
2012-01-18 16:39:04 +01:00
log_error ( " [%s:%u] Write file requires argument. " , fname , line ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2012-01-18 16:39:04 +01:00
}
break ;
2014-06-10 23:02:40 +02:00
case COPY_FILES :
2015-01-09 08:00:37 +01:00
if ( ! i . argument ) {
i . argument = strappend ( " /usr/share/factory/ " , i . path ) ;
if ( ! i . argument )
2014-06-20 15:57:43 +02:00
return log_oom ( ) ;
2015-01-09 08:00:37 +01:00
} else if ( ! path_is_absolute ( i . argument ) ) {
2014-06-10 23:02:40 +02:00
log_error ( " [%s:%u] Source path is not absolute. " , fname , line ) ;
return - EBADMSG ;
}
2015-01-09 08:00:37 +01:00
path_kill_slashes ( i . argument ) ;
2014-06-10 23:02:40 +02:00
break ;
2012-01-17 15:04:12 +01:00
case CREATE_CHAR_DEVICE :
case CREATE_BLOCK_DEVICE : {
unsigned major , minor ;
2015-01-09 08:00:37 +01:00
if ( ! i . argument ) {
2012-01-17 15:04:12 +01:00
log_error ( " [%s:%u] Device file requires argument. " , fname , line ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2012-01-17 15:04:12 +01:00
}
2015-01-09 08:00:37 +01:00
if ( sscanf ( i . argument , " %u:%u " , & major , & minor ) ! = 2 ) {
log_error ( " [%s:%u] Can't parse device file major/minor '%s'. " , fname , line , i . argument ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2012-01-17 15:04:12 +01:00
}
2015-01-09 08:00:37 +01:00
i . major_minor = makedev ( major , minor ) ;
2012-01-17 15:04:12 +01:00
break ;
}
2014-12-04 10:32:10 +01:00
case SET_XATTR :
2015-01-18 08:10:00 +01:00
case RECURSIVE_SET_XATTR :
2015-01-09 08:00:37 +01:00
if ( ! i . argument ) {
2014-12-04 10:32:10 +01:00
log_error ( " [%s:%u] Set extended attribute requires argument. " , fname , line ) ;
return - EBADMSG ;
}
2015-04-10 16:03:24 +02:00
r = parse_xattrs_from_arg ( & i ) ;
2014-12-04 10:32:10 +01:00
if ( r < 0 )
return r ;
break ;
2015-01-18 05:27:39 +01:00
case SET_ACL :
2015-01-18 08:10:00 +01:00
case RECURSIVE_SET_ACL :
2015-01-18 05:27:39 +01:00
if ( ! i . argument ) {
log_error ( " [%s:%u] Set ACLs requires argument. " , fname , line ) ;
return - EBADMSG ;
}
2015-04-10 16:03:24 +02:00
r = parse_acls_from_arg ( & i ) ;
2015-01-18 05:27:39 +01:00
if ( r < 0 )
return r ;
break ;
2015-04-08 22:35:52 +02:00
case SET_ATTRIBUTE :
case RECURSIVE_SET_ATTRIBUTE :
2015-03-16 20:33:50 +01:00
if ( ! i . argument ) {
2015-04-08 22:35:52 +02:00
log_error ( " [%s:%u] Set file attribute requires argument. " , fname , line ) ;
2015-03-16 20:33:50 +01:00
return - EBADMSG ;
}
2015-04-10 16:03:24 +02:00
r = parse_attribute_from_arg ( & i ) ;
2015-03-16 20:33:50 +01:00
if ( r < 0 )
return r ;
break ;
2011-12-16 18:27:35 +01:00
default :
2015-01-24 07:54:05 +01:00
log_error ( " [%s:%u] Unknown command type '%c'. " , fname , line , ( char ) i . type ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2010-10-18 22:38:41 +02:00
}
2012-01-17 15:04:12 +01:00
2015-01-09 08:00:37 +01:00
if ( ! path_is_absolute ( i . path ) ) {
log_error ( " [%s:%u] Path '%s' not absolute. " , fname , line , i . path ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
path_kill_slashes ( i . path ) ;
2010-10-18 22:38:41 +02:00
2015-01-09 08:00:37 +01:00
if ( ! should_include_path ( i . path ) )
2013-03-31 22:29:46 +02:00
return 0 ;
2010-09-28 02:34:02 +02:00
2014-03-14 05:32:13 +01:00
if ( arg_root ) {
2014-06-10 22:50:46 +02:00
char * p ;
2015-05-13 17:42:10 +02:00
p = prefix_root ( arg_root , i . path ) ;
2014-03-14 05:32:13 +01:00
if ( ! p )
return log_oom ( ) ;
2015-01-09 08:00:37 +01:00
free ( i . path ) ;
i . path = p ;
2014-03-14 05:32:13 +01:00
}
2015-04-10 16:04:16 +02:00
if ( ! isempty ( user ) & & ! streq ( user , " - " ) ) {
2011-07-23 01:17:59 +02:00
const char * u = user ;
2015-01-09 08:00:37 +01:00
r = get_user_creds ( & u , & i . uid , NULL , NULL , NULL ) ;
2011-07-23 01:17:59 +02:00
if ( r < 0 ) {
2010-10-18 22:38:41 +02:00
log_error ( " [%s:%u] Unknown user '%s'. " , fname , line , user ) ;
2013-03-31 22:29:46 +02:00
return r ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
i . uid_set = true ;
2010-10-18 22:38:41 +02:00
}
2015-04-10 16:04:16 +02:00
if ( ! isempty ( group ) & & ! streq ( group , " - " ) ) {
2011-07-23 01:17:59 +02:00
const char * g = group ;
2015-01-09 08:00:37 +01:00
r = get_group_creds ( & g , & i . gid ) ;
2011-07-23 01:17:59 +02:00
if ( r < 0 ) {
2010-10-18 22:38:41 +02:00
log_error ( " [%s:%u] Unknown group '%s'. " , fname , line , group ) ;
2013-03-31 22:29:46 +02:00
return r ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
i . gid_set = true ;
2010-10-18 22:38:41 +02:00
}
2015-04-10 16:04:16 +02:00
if ( ! isempty ( mode ) & & ! streq ( mode , " - " ) ) {
2014-06-11 10:14:07 +02:00
const char * mm = mode ;
2010-10-18 22:38:41 +02:00
unsigned m ;
2014-06-11 10:14:07 +02:00
if ( * mm = = ' ~ ' ) {
2015-01-09 08:00:37 +01:00
i . mask_perms = true ;
2014-06-11 10:14:07 +02:00
mm + + ;
}
2015-04-10 14:43:06 +02:00
if ( parse_mode ( mm , & m ) < 0 ) {
2010-10-18 22:38:41 +02:00
log_error ( " [%s:%u] Invalid mode '%s'. " , fname , line , mode ) ;
2015-04-10 14:43:06 +02:00
return - EBADMSG ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
i . mode = m ;
i . mode_set = true ;
2010-10-18 22:38:41 +02:00
} else
2015-10-21 19:46:23 +02:00
i . mode = IN_SET ( i . type , CREATE_DIRECTORY , TRUNCATE_DIRECTORY , CREATE_SUBVOLUME , CREATE_SUBVOLUME_INHERIT_QUOTA , CREATE_SUBVOLUME_NEW_QUOTA ) ? 0755 : 0644 ;
2010-10-18 22:38:41 +02:00
2015-04-10 16:04:16 +02:00
if ( ! isempty ( age ) & & ! streq ( age , " - " ) ) {
2012-06-20 09:05:50 +02:00
const char * a = age ;
if ( * a = = ' ~ ' ) {
2015-01-09 08:00:37 +01:00
i . keep_first_level = true ;
2012-06-20 09:05:50 +02:00
a + + ;
}
2015-01-09 08:00:37 +01:00
if ( parse_sec ( a , & i . age ) < 0 ) {
2010-10-18 22:38:41 +02:00
log_error ( " [%s:%u] Invalid age '%s'. " , fname , line , age ) ;
2013-03-31 22:29:46 +02:00
return - EBADMSG ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
i . age_set = true ;
2010-10-18 22:38:41 +02:00
}
2015-01-09 08:00:37 +01:00
h = needs_glob ( i . type ) ? globs : items ;
2011-04-08 04:49:43 +02:00
2015-04-22 18:18:56 +02:00
existing = ordered_hashmap_get ( h , i . path ) ;
2012-01-17 15:04:12 +01:00
if ( existing ) {
2015-01-09 08:00:37 +01:00
unsigned n ;
for ( n = 0 ; n < existing - > count ; n + + ) {
2015-03-05 14:58:56 +01:00
if ( ! item_compatible ( existing - > items + n , & i ) ) {
2015-01-09 07:10:02 +01:00
log_warning ( " [%s:%u] Duplicate line for path \" %s \" , ignoring. " ,
2015-01-09 08:00:37 +01:00
fname , line , i . path ) ;
2015-03-05 14:58:56 +01:00
return 0 ;
}
2014-12-04 10:32:10 +01:00
}
} else {
2015-01-09 08:00:37 +01:00
existing = new0 ( ItemArray , 1 ) ;
2015-04-22 18:18:56 +02:00
r = ordered_hashmap_put ( h , i . path , existing ) ;
2015-01-09 08:00:37 +01:00
if ( r < 0 )
return log_oom ( ) ;
2011-04-08 04:49:43 +02:00
}
2015-01-09 08:00:37 +01:00
if ( ! GREEDY_REALLOC ( existing - > items , existing - > size , existing - > count + 1 ) )
return log_oom ( ) ;
2010-09-28 02:34:02 +02:00
2015-01-09 08:00:37 +01:00
memcpy ( existing - > items + existing - > count + + , & i , sizeof ( i ) ) ;
2015-04-11 00:57:05 +02:00
/* Sort item array, to enforce stable ordering of application */
qsort_safe ( existing - > items , existing - > count , sizeof ( Item ) , item_compare ) ;
2015-04-10 16:22:22 +02:00
2015-01-09 08:00:37 +01:00
zero ( i ) ;
2013-03-31 22:29:46 +02:00
return 0 ;
2010-09-28 02:34:02 +02:00
}
2014-08-02 17:12:21 +02:00
static void help ( void ) {
2011-02-13 15:08:15 +01:00
printf ( " %s [OPTIONS...] [CONFIGURATION FILE...] \n \n "
" Creates, deletes and cleans up volatile and temporary files and directories. \n \n "
2013-07-24 17:19:24 +02:00
" -h --help Show this help \n "
2013-11-06 18:28:39 +01:00
" --version Show package version \n "
2013-07-24 17:19:24 +02:00
" --create Create marked files/directories \n "
" --clean Clean up marked directories \n "
" --remove Remove marked files/directories \n "
2013-12-30 19:00:38 +01:00
" --boot Execute actions only safe at boot \n "
2015-01-28 03:22:08 +01:00
" --prefix=PATH Only apply rules with the specified prefix \n "
" --exclude-prefix=PATH Ignore rules with the specified prefix \n "
2014-03-14 05:32:13 +01:00
" --root=PATH Operate on an alternate filesystem root \n " ,
2010-10-18 22:38:41 +02:00
program_invocation_short_name ) ;
}
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
2013-11-06 18:28:39 +01:00
ARG_VERSION = 0x100 ,
2010-10-18 22:38:41 +02:00
ARG_CREATE ,
ARG_CLEAN ,
2011-02-13 14:00:54 +01:00
ARG_REMOVE ,
2013-12-30 19:00:38 +01:00
ARG_BOOT ,
2013-07-24 17:19:24 +02:00
ARG_PREFIX ,
ARG_EXCLUDE_PREFIX ,
2014-03-14 05:32:13 +01:00
ARG_ROOT ,
2010-10-18 22:38:41 +02:00
} ;
static const struct option options [ ] = {
2013-07-24 17:19:24 +02:00
{ " help " , no_argument , NULL , ' h ' } ,
2013-11-06 18:28:39 +01:00
{ " version " , no_argument , NULL , ARG_VERSION } ,
2013-07-24 17:19:24 +02:00
{ " create " , no_argument , NULL , ARG_CREATE } ,
{ " clean " , no_argument , NULL , ARG_CLEAN } ,
{ " remove " , no_argument , NULL , ARG_REMOVE } ,
2013-12-30 19:00:38 +01:00
{ " boot " , no_argument , NULL , ARG_BOOT } ,
2013-07-24 17:19:24 +02:00
{ " prefix " , required_argument , NULL , ARG_PREFIX } ,
{ " exclude-prefix " , required_argument , NULL , ARG_EXCLUDE_PREFIX } ,
2014-03-14 05:32:13 +01:00
{ " root " , required_argument , NULL , ARG_ROOT } ,
2013-11-06 18:28:39 +01:00
{ }
2010-10-18 22:38:41 +02:00
} ;
2015-10-22 19:28:31 +02:00
int c , r ;
2010-10-18 22:38:41 +02:00
assert ( argc > = 0 ) ;
assert ( argv ) ;
2014-08-02 17:12:21 +02:00
while ( ( c = getopt_long ( argc , argv , " h " , options , NULL ) ) > = 0 )
2010-10-18 22:38:41 +02:00
switch ( c ) {
case ' h ' :
2014-08-02 17:12:21 +02:00
help ( ) ;
return 0 ;
2013-11-06 18:28:39 +01:00
case ARG_VERSION :
2015-09-23 03:01:06 +02:00
return version ( ) ;
2010-10-18 22:38:41 +02:00
case ARG_CREATE :
arg_create = true ;
break ;
case ARG_CLEAN :
arg_clean = true ;
break ;
case ARG_REMOVE :
arg_remove = true ;
break ;
2013-12-30 19:00:38 +01:00
case ARG_BOOT :
arg_boot = true ;
2013-12-21 02:25:39 +01:00
break ;
2011-02-13 14:00:54 +01:00
case ARG_PREFIX :
2014-06-11 01:26:28 +02:00
if ( strv_push ( & arg_include_prefixes , optarg ) < 0 )
2013-07-24 17:10:05 +02:00
return log_oom ( ) ;
2011-02-13 14:00:54 +01:00
break ;
2013-07-24 17:19:24 +02:00
case ARG_EXCLUDE_PREFIX :
2014-06-11 01:26:28 +02:00
if ( strv_push ( & arg_exclude_prefixes , optarg ) < 0 )
2013-07-24 17:19:24 +02:00
return log_oom ( ) ;
break ;
2014-03-14 05:32:13 +01:00
case ARG_ROOT :
2015-10-22 19:54:29 +02:00
r = parse_path_argument_and_warn ( optarg , true , & arg_root ) ;
2015-10-22 19:28:31 +02:00
if ( r < 0 )
2015-10-22 19:54:29 +02:00
return r ;
2014-03-14 05:32:13 +01:00
break ;
2010-10-18 22:38:41 +02:00
case ' ? ' :
return - EINVAL ;
default :
2013-11-06 18:28:39 +01:00
assert_not_reached ( " Unhandled option " ) ;
2010-10-18 22:38:41 +02:00
}
if ( ! arg_clean & & ! arg_create & & ! arg_remove ) {
2011-02-21 15:32:17 +01:00
log_error ( " You need to specify at least one of --clean, --create or --remove. " ) ;
2010-10-18 22:38:41 +02:00
return - EINVAL ;
}
return 1 ;
}
2011-02-13 14:00:54 +01:00
static int read_config_file ( const char * fn , bool ignore_enoent ) {
2013-09-17 18:02:02 +02:00
_cleanup_fclose_ FILE * f = NULL ;
char line [ LINE_MAX ] ;
2013-01-18 16:13:08 +01:00
Iterator iterator ;
2013-09-17 18:02:02 +02:00
unsigned v = 0 ;
2013-01-18 16:13:08 +01:00
Item * i ;
2013-09-17 18:02:02 +02:00
int r ;
2011-02-13 14:00:54 +01:00
assert ( fn ) ;
2014-03-14 05:32:13 +01:00
r = search_and_fopen_nulstr ( fn , " re " , arg_root , conf_file_dirs , & f ) ;
2013-02-11 23:48:36 +01:00
if ( r < 0 ) {
2015-01-24 07:54:05 +01:00
if ( ignore_enoent & & r = = - ENOENT ) {
log_debug_errno ( r , " Failed to open \" %s \" : %m " , fn ) ;
2011-02-13 14:00:54 +01:00
return 0 ;
2015-01-24 07:54:05 +01:00
}
2011-02-13 14:00:54 +01:00
2014-11-28 19:13:53 +01:00
return log_error_errno ( r , " Failed to open '%s', ignoring: %m " , fn ) ;
2011-02-13 14:00:54 +01:00
}
2015-01-24 07:54:05 +01:00
log_debug ( " Reading config file \" %s \" . " , fn ) ;
2011-02-13 14:00:54 +01:00
2013-09-17 18:02:02 +02:00
FOREACH_LINE ( line , f , break ) {
char * l ;
2011-02-13 14:00:54 +01:00
int k ;
v + + ;
l = strstrip ( line ) ;
if ( * l = = ' # ' | | * l = = 0 )
continue ;
2013-09-17 18:02:02 +02:00
k = parse_line ( fn , v , l ) ;
if ( k < 0 & & r = = 0 )
r = k ;
2011-02-13 14:00:54 +01:00
}
2013-01-18 16:13:08 +01:00
/* we have to determine age parameter for each entry of type X */
2015-04-22 18:18:56 +02:00
ORDERED_HASHMAP_FOREACH ( i , globs , iterator ) {
2013-01-18 16:13:08 +01:00
Iterator iter ;
Item * j , * candidate_item = NULL ;
if ( i - > type ! = IGNORE_DIRECTORY_PATH )
continue ;
2015-04-22 18:18:56 +02:00
ORDERED_HASHMAP_FOREACH ( j , items , iter ) {
2015-10-21 19:46:23 +02:00
if ( ! IN_SET ( j - > type , CREATE_DIRECTORY , TRUNCATE_DIRECTORY , CREATE_SUBVOLUME , CREATE_SUBVOLUME_INHERIT_QUOTA , CREATE_SUBVOLUME_NEW_QUOTA ) )
2013-01-18 16:13:08 +01:00
continue ;
if ( path_equal ( j - > path , i - > path ) ) {
candidate_item = j ;
break ;
}
if ( ( ! candidate_item & & path_startswith ( i - > path , j - > path ) ) | |
( candidate_item & & path_startswith ( j - > path , candidate_item - > path ) & & ( fnmatch ( i - > path , j - > path , FNM_PATHNAME | FNM_PERIOD ) = = 0 ) ) )
candidate_item = j ;
}
2014-09-09 11:09:37 +02:00
if ( candidate_item & & candidate_item - > age_set ) {
2013-01-18 16:13:08 +01:00
i - > age = candidate_item - > age ;
i - > age_set = true ;
}
}
2011-02-13 14:00:54 +01:00
if ( ferror ( f ) ) {
2014-11-28 19:29:59 +01:00
log_error_errno ( errno , " Failed to read from file %s: %m " , fn ) ;
2011-02-13 14:00:54 +01:00
if ( r = = 0 )
r = - EIO ;
}
return r ;
}
2010-09-28 02:34:02 +02:00
int main ( int argc , char * argv [ ] ) {
2013-02-11 23:48:36 +01:00
int r , k ;
2015-01-09 08:00:37 +01:00
ItemArray * a ;
2010-10-18 22:38:41 +02:00
Iterator iterator ;
2012-01-11 22:07:35 +01:00
r = parse_argv ( argc , argv ) ;
if ( r < = 0 )
2014-06-12 23:07:17 +02:00
goto finish ;
2010-09-28 02:34:02 +02:00
2011-02-12 09:31:38 +01:00
log_set_target ( LOG_TARGET_AUTO ) ;
2010-09-28 02:34:02 +02:00
log_parse_environment ( ) ;
log_open ( ) ;
2011-08-01 20:52:18 +02:00
umask ( 0022 ) ;
2014-10-23 10:23:46 +02:00
mac_selinux_init ( NULL ) ;
2010-09-28 02:34:02 +02:00
2015-04-22 18:18:56 +02:00
items = ordered_hashmap_new ( & string_hash_ops ) ;
globs = ordered_hashmap_new ( & string_hash_ops ) ;
2011-02-12 09:31:25 +01:00
if ( ! items | | ! globs ) {
2013-02-11 23:48:36 +01:00
r = log_oom ( ) ;
2010-10-18 22:38:41 +02:00
goto finish ;
}
2013-02-11 23:48:36 +01:00
r = 0 ;
2010-09-28 02:34:02 +02:00
2011-02-13 14:00:54 +01:00
if ( optind < argc ) {
int j ;
2010-09-28 02:34:02 +02:00
2012-06-09 04:31:19 +02:00
for ( j = optind ; j < argc ; j + + ) {
2013-02-11 23:48:36 +01:00
k = read_config_file ( argv [ j ] , false ) ;
if ( k < 0 & & r = = 0 )
r = k ;
2012-06-09 04:31:19 +02:00
}
2010-09-28 02:34:02 +02:00
2011-02-13 14:00:54 +01:00
} else {
2013-02-11 23:48:36 +01:00
_cleanup_strv_free_ char * * files = NULL ;
char * * f ;
2010-09-28 02:34:02 +02:00
2014-03-14 05:32:13 +01:00
r = conf_files_list_nulstr ( & files , " .conf " , arg_root , conf_file_dirs ) ;
2011-04-28 23:51:24 +02:00
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_error_errno ( r , " Failed to enumerate tmpfiles.d files: %m " ) ;
2011-04-28 23:51:24 +02:00
goto finish ;
}
2010-10-18 22:38:41 +02:00
2011-04-25 21:38:21 +02:00
STRV_FOREACH ( f , files ) {
2013-02-11 23:48:36 +01:00
k = read_config_file ( * f , true ) ;
if ( k < 0 & & r = = 0 )
r = k ;
2010-09-28 02:34:02 +02:00
}
2011-04-25 21:38:21 +02:00
}
2010-09-28 02:34:02 +02:00
2015-04-10 16:22:22 +02:00
/* The non-globbing ones usually create things, hence we apply
* them first */
2015-04-22 18:18:56 +02:00
ORDERED_HASHMAP_FOREACH ( a , items , iterator ) {
2015-01-09 08:00:37 +01:00
k = process_item_array ( a ) ;
2015-01-06 15:53:12 +01:00
if ( k < 0 & & r = = 0 )
r = k ;
}
2011-02-12 09:31:25 +01:00
2015-04-10 16:22:22 +02:00
/* The globbing ones usually alter things, hence we apply them
* second . */
2015-04-22 18:18:56 +02:00
ORDERED_HASHMAP_FOREACH ( a , globs , iterator ) {
2015-01-09 08:00:37 +01:00
k = process_item_array ( a ) ;
2015-01-06 15:53:12 +01:00
if ( k < 0 & & r = = 0 )
r = k ;
}
2010-10-18 22:38:41 +02:00
2010-09-28 02:34:02 +02:00
finish :
2015-04-22 18:18:56 +02:00
while ( ( a = ordered_hashmap_steal_first ( items ) ) )
2015-01-09 08:00:37 +01:00
item_array_free ( a ) ;
2010-10-18 22:38:41 +02:00
2015-04-22 18:18:56 +02:00
while ( ( a = ordered_hashmap_steal_first ( globs ) ) )
2015-01-09 08:00:37 +01:00
item_array_free ( a ) ;
2011-02-14 21:55:06 +01:00
2015-04-22 18:18:56 +02:00
ordered_hashmap_free ( items ) ;
ordered_hashmap_free ( globs ) ;
2010-09-28 02:34:02 +02:00
2014-06-11 01:26:28 +02:00
free ( arg_include_prefixes ) ;
free ( arg_exclude_prefixes ) ;
2014-03-14 05:32:13 +01:00
free ( arg_root ) ;
2013-07-24 17:10:05 +02:00
2011-02-14 21:55:06 +01:00
set_free_free ( unix_sockets ) ;
2014-10-23 10:23:46 +02:00
mac_selinux_finish ( ) ;
2010-10-19 19:36:45 +02:00
2013-02-11 23:48:36 +01:00
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
2010-09-28 02:34:02 +02:00
}