1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "chattr-util.h"
10 #include "errno-util.h"
13 #include "string-util.h"
15 int chattr_full(const char *path
,
19 unsigned *ret_previous
,
21 ChattrApplyFlags flags
) {
23 _cleanup_close_
int fd_will_close
= -1;
24 unsigned old_attr
, new_attr
;
27 assert(path
|| fd
>= 0);
30 fd
= fd_will_close
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
35 if (fstat(fd
, &st
) < 0)
38 /* Explicitly check whether this is a regular file or directory. If it is anything else (such
39 * as a device node or fifo), then the ioctl will not hit the file systems but possibly
40 * drivers, where the ioctl might have different effects. Notably, DRM is using the same
43 if (!S_ISDIR(st
.st_mode
) && !S_ISREG(st
.st_mode
))
46 if (mask
== 0 && !ret_previous
&& !ret_final
)
49 if (ioctl(fd
, FS_IOC_GETFLAGS
, &old_attr
) < 0)
52 new_attr
= (old_attr
& ~mask
) | (value
& mask
);
53 if (new_attr
== old_attr
) {
55 *ret_previous
= old_attr
;
57 *ret_final
= old_attr
;
61 if (ioctl(fd
, FS_IOC_SETFLAGS
, &new_attr
) >= 0) {
63 *ret_previous
= old_attr
;
65 *ret_final
= new_attr
;
69 if ((errno
!= EINVAL
&& !ERRNO_IS_NOT_SUPPORTED(errno
)) ||
70 !FLAGS_SET(flags
, CHATTR_FALLBACK_BITWISE
))
73 /* When -EINVAL is returned, we assume that incompatible attributes are simultaneously
74 * specified. E.g., compress(c) and nocow(C) attributes cannot be set to files on btrfs.
75 * As a fallback, let's try to set attributes one by one.
77 * Also, when we get EOPNOTSUPP (or a similar error code) we assume a flag might just not be
78 * supported, and we can ignore it too */
80 unsigned current_attr
= old_attr
;
81 for (unsigned i
= 0; i
< sizeof(unsigned) * 8; i
++) {
82 unsigned new_one
, mask_one
= 1u << i
;
84 if (!FLAGS_SET(mask
, mask_one
))
87 new_one
= UPDATE_FLAG(current_attr
, mask_one
, FLAGS_SET(value
, mask_one
));
88 if (new_one
== current_attr
)
91 if (ioctl(fd
, FS_IOC_SETFLAGS
, &new_one
) < 0) {
92 if (errno
!= EINVAL
&& !ERRNO_IS_NOT_SUPPORTED(errno
))
95 log_full_errno(FLAGS_SET(flags
, CHATTR_WARN_UNSUPPORTED_FLAGS
) ? LOG_WARNING
: LOG_DEBUG
,
97 "Unable to set file attribute 0x%x on %s, ignoring: %m", mask_one
, strna(path
));
101 if (ioctl(fd
, FS_IOC_GETFLAGS
, ¤t_attr
) < 0)
106 *ret_previous
= old_attr
;
108 *ret_final
= current_attr
;
110 return current_attr
== new_attr
? 1 : -ENOANO
; /* -ENOANO indicates that some attributes cannot be set. */
113 int read_attr_fd(int fd
, unsigned *ret
) {
118 if (fstat(fd
, &st
) < 0)
121 if (!S_ISDIR(st
.st_mode
) && !S_ISREG(st
.st_mode
))
124 return RET_NERRNO(ioctl(fd
, FS_IOC_GETFLAGS
, ret
));
127 int read_attr_path(const char *p
, unsigned *ret
) {
128 _cleanup_close_
int fd
= -1;
133 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
137 return read_attr_fd(fd
, ret
);