diff options
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index bb4d41ae07..25c8cfa694 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -36,6 +36,7 @@ #include <fnmatch.h> #include <sys/stat.h> #include <sys/xattr.h> +#include <linux/fs.h> #include "log.h" #include "util.h" @@ -86,6 +87,8 @@ typedef enum ItemType { ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */ RELABEL_PATH = 'z', RECURSIVE_RELABEL_PATH = 'Z', + SET_ATTRIB = 'h', + RECURSIVE_SET_ATTRIB = 'H', } ItemType; typedef struct Item { @@ -104,12 +107,15 @@ typedef struct Item { usec_t age; dev_t major_minor; + unsigned long attrib_value; + unsigned long attrib_mask; bool uid_set:1; bool gid_set:1; bool mode_set:1; bool age_set:1; bool mask_perms:1; + bool attrib_set:1; bool keep_first_level:1; @@ -762,6 +768,127 @@ static int path_set_acls(Item *item, const char *path) { return r; } +#define ALL_ATTRIBS \ + 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 + +static int get_attrib_from_arg(Item *item) { + static const unsigned attributes[] = { + [(uint8_t)'A'] = FS_NOATIME_FL, /* do not update atime */ + [(uint8_t)'S'] = FS_SYNC_FL, /* Synchronous updates */ + [(uint8_t)'D'] = FS_DIRSYNC_FL, /* dirsync behaviour (directories only) */ + [(uint8_t)'a'] = FS_APPEND_FL, /* writes to file may only append */ + [(uint8_t)'c'] = FS_COMPR_FL, /* Compress file */ + [(uint8_t)'d'] = FS_NODUMP_FL, /* do not dump file */ + [(uint8_t)'e'] = FS_EXTENT_FL, /* Top of directory hierarchies*/ + [(uint8_t)'i'] = FS_IMMUTABLE_FL, /* Immutable file */ + [(uint8_t)'j'] = FS_JOURNAL_DATA_FL, /* Reserved for ext3 */ + [(uint8_t)'s'] = FS_SECRM_FL, /* Secure deletion */ + [(uint8_t)'u'] = FS_UNRM_FL, /* Undelete */ + [(uint8_t)'t'] = FS_NOTAIL_FL, /* file tail should not be merged */ + [(uint8_t)'T'] = FS_TOPDIR_FL, /* Top of directory hierarchies*/ + [(uint8_t)'C'] = FS_NOCOW_FL, /* Do not cow file */ + }; + char *p = item->argument; + enum { + MODE_ADD, + MODE_DEL, + MODE_SET + } mode = MODE_ADD; + unsigned long value = 0, mask = 0; + + if (!p) { + log_error("\"%s\": setting ATTR need an argument", item->path); + return -EINVAL; + } + + if (*p == '+') { + mode = MODE_ADD; + p++; + } else if (*p == '-') { + mode = MODE_DEL; + p++; + } else if (*p == '=') { + mode = MODE_SET; + p++; + } + + if (!*p && mode != MODE_SET) { + log_error("\"%s\": setting ATTR: argument is empty", item->path); + return -EINVAL; + } + for (; *p ; p++) { + if ((uint8_t)*p > ELEMENTSOF(attributes) || attributes[(uint8_t)*p] == 0) { + log_error("\"%s\": setting ATTR: unknown attr '%c'", item->path, *p); + return -EINVAL; + } + if (mode == MODE_ADD || mode == MODE_SET) + value |= attributes[(uint8_t)*p]; + else + value &= ~attributes[(uint8_t)*p]; + mask |= attributes[(uint8_t)*p]; + } + + if (mode == MODE_SET) + mask |= ALL_ATTRIBS; + + assert(mask); + + item->attrib_mask = mask; + item->attrib_value = value; + item->attrib_set = true; + + return 0; + +} + +static int path_set_attrib(Item *item, const char *path) { + _cleanup_close_ int fd = -1; + int r; + unsigned f; + struct stat st; + + /* do nothing */ + if (item->attrib_mask == 0 || !item->attrib_set) + return 0; + /* + * It is OK to ignore an lstat() error, because the error + * will be catch by the open() below anyway + */ + if (lstat(path, &st) == 0 && + !S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { + return 0; + } + + fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC); + + if (fd < 0) + return log_error_errno(errno, "Cannot open \"%s\": %m", path); + + f = item->attrib_value & item->attrib_mask; + if (!S_ISDIR(st.st_mode)) + f &= ~FS_DIRSYNC_FL; + r = change_attr_fd(fd, f, item->attrib_mask); + if (r < 0) + return log_error_errno(errno, + "Cannot set attrib for \"%s\", value=0x%08lx, mask=0x%08lx: %m", + path, item->attrib_value, item->attrib_mask); + + return 0; +} + static int write_one_file(Item *i, const char *path) { _cleanup_close_ int fd = -1; int flags, r = 0; @@ -1203,6 +1330,18 @@ static int create_item(Item *i) { if (r < 0) return r; break; + + case SET_ATTRIB: + r = glob_item(i, path_set_attrib, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_ATTRIB: + r = glob_item(i, path_set_attrib, true); + if (r < 0) + return r; + break; } return 0; @@ -1267,6 +1406,8 @@ static int remove_item(Item *i) { case RECURSIVE_SET_XATTR: case SET_ACL: case RECURSIVE_SET_ACL: + case SET_ATTRIB: + case RECURSIVE_SET_ATTRIB: break; case REMOVE_PATH: @@ -1644,6 +1785,17 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return r; break; + case SET_ATTRIB: + case RECURSIVE_SET_ATTRIB: + if (!i.argument) { + log_error("[%s:%u] Set attrib requires argument.", fname, line); + return -EBADMSG; + } + r = get_attrib_from_arg(&i); + if (r < 0) + return r; + break; + default: log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); return -EBADMSG; |