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) {
64 /* Some filesystems (BTRFS) silently fail when a flag cannot be set. Let's make sure our
65 * changes actually went through by querying the flags again and verifying they're equal to
66 * the flags we tried to configure. */
68 if (ioctl(fd
, FS_IOC_GETFLAGS
, &attr
) < 0)
71 if (new_attr
== attr
) {
73 *ret_previous
= old_attr
;
75 *ret_final
= new_attr
;
79 /* Trigger the fallback logic. */
83 if ((errno
!= EINVAL
&& !ERRNO_IS_NOT_SUPPORTED(errno
)) ||
84 !FLAGS_SET(flags
, CHATTR_FALLBACK_BITWISE
))
87 /* When -EINVAL is returned, we assume that incompatible attributes are simultaneously
88 * specified. E.g., compress(c) and nocow(C) attributes cannot be set to files on btrfs.
89 * As a fallback, let's try to set attributes one by one.
91 * Also, when we get EOPNOTSUPP (or a similar error code) we assume a flag might just not be
92 * supported, and we can ignore it too */
94 unsigned current_attr
= old_attr
;
95 for (unsigned i
= 0; i
< sizeof(unsigned) * 8; i
++) {
96 unsigned new_one
, mask_one
= 1u << i
;
98 if (!FLAGS_SET(mask
, mask_one
))
101 new_one
= UPDATE_FLAG(current_attr
, mask_one
, FLAGS_SET(value
, mask_one
));
102 if (new_one
== current_attr
)
105 if (ioctl(fd
, FS_IOC_SETFLAGS
, &new_one
) < 0) {
106 if (errno
!= EINVAL
&& !ERRNO_IS_NOT_SUPPORTED(errno
))
109 log_full_errno(FLAGS_SET(flags
, CHATTR_WARN_UNSUPPORTED_FLAGS
) ? LOG_WARNING
: LOG_DEBUG
,
111 "Unable to set file attribute 0x%x on %s, ignoring: %m", mask_one
, strna(path
));
115 if (ioctl(fd
, FS_IOC_GETFLAGS
, ¤t_attr
) < 0)
120 *ret_previous
= old_attr
;
122 *ret_final
= current_attr
;
124 return current_attr
== new_attr
? 1 : -ENOANO
; /* -ENOANO indicates that some attributes cannot be set. */
127 int read_attr_fd(int fd
, unsigned *ret
) {
132 if (fstat(fd
, &st
) < 0)
135 if (!S_ISDIR(st
.st_mode
) && !S_ISREG(st
.st_mode
))
138 return RET_NERRNO(ioctl(fd
, FS_IOC_GETFLAGS
, ret
));
141 int read_attr_path(const char *p
, unsigned *ret
) {
142 _cleanup_close_
int fd
= -1;
147 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
151 return read_attr_fd(fd
, ret
);