]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/xattr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
11 #include "alloc-util.h"
15 #include "sparse-endian.h"
16 #include "stdio-util.h"
17 #include "string-util.h"
18 #include "time-util.h"
19 #include "xattr-util.h"
21 int getxattr_malloc ( const char * path
, const char * name
, char ** value
, bool allow_symlink
) {
30 for ( l
= 100 ; ; l
= ( size_t ) n
+ 1 ) {
36 n
= lgetxattr ( path
, name
, v
, l
);
38 n
= getxattr ( path
, name
, v
, l
);
40 if ( n
>= 0 && ( size_t ) n
< l
) {
47 if ( n
< 0 && errno
!= ERANGE
)
51 n
= lgetxattr ( path
, name
, NULL
, 0 );
53 n
= getxattr ( path
, name
, NULL
, 0 );
59 int fgetxattr_malloc ( int fd
, const char * name
, char ** value
) {
68 for ( l
= 100 ; ; l
= ( size_t ) n
+ 1 ) {
73 n
= fgetxattr ( fd
, name
, v
, l
);
75 if ( n
>= 0 && ( size_t ) n
< l
) {
82 if ( n
< 0 && errno
!= ERANGE
)
85 n
= fgetxattr ( fd
, name
, NULL
, 0 );
94 const char * attribute
,
95 void * value
, size_t size
,
99 char fn
[ STRLEN ( "/proc/self/fd/" ) + DECIMAL_STR_MAX ( int ) + 1 ];
100 _cleanup_close_
int fd
= - 1 ;
103 /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
105 if ( flags
& ~( AT_SYMLINK_NOFOLLOW
| AT_EMPTY_PATH
))
108 if ( isempty ( filename
)) {
109 if (!( flags
& AT_EMPTY_PATH
))
112 xsprintf ( fn
, "/proc/self/fd/%i" , dirfd
);
114 fd
= openat ( dirfd
, filename
, O_CLOEXEC
| O_PATH
|( flags
& AT_SYMLINK_NOFOLLOW
? O_NOFOLLOW
: 0 ));
118 xsprintf ( fn
, "/proc/self/fd/%i" , fd
);
121 l
= getxattr ( fn
, attribute
, value
, size
);
129 static int parse_crtime ( le64_t le
, usec_t
* usec
) {
135 if ( IN_SET ( u
, 0 , ( uint64_t ) - 1 ))
142 int fd_getcrtime_at ( int dirfd
, const char * name
, usec_t
* ret
, int flags
) {
144 #if HAS_FEATURE_MEMORY_SANITIZER
146 # warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
156 if ( flags
& ~( AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
))
159 /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
160 * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
161 * implemented on various file systems on the lower level since a while, but never was accessible). However, we
162 * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
163 * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
164 * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
165 * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
166 * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
169 if ( statx ( dirfd
, strempty ( name
), flags
| AT_STATX_DONT_SYNC
, STATX_BTIME
, & sx
) >= 0 &&
170 ( sx
. stx_mask
& STATX_BTIME
) &&
171 sx
. stx_btime
. tv_sec
!= 0 )
172 a
= ( usec_t
) sx
. stx_btime
. tv_sec
* USEC_PER_SEC
+
173 ( usec_t
) sx
. stx_btime
. tv_nsec
/ NSEC_PER_USEC
;
177 r
= fgetxattrat_fake ( dirfd
, name
, "user.crtime_usec" , & le
, sizeof ( le
), flags
, & n
);
182 r
= parse_crtime ( le
, & b
);
185 if ( a
!= USEC_INFINITY
) {
193 if ( a
!= USEC_INFINITY
)
201 int fd_getcrtime ( int fd
, usec_t
* ret
) {
202 return fd_getcrtime_at ( fd
, NULL
, ret
, AT_EMPTY_PATH
);
205 int path_getcrtime ( const char * p
, usec_t
* ret
) {
206 return fd_getcrtime_at ( AT_FDCWD
, p
, ret
, 0 );
209 int fd_setcrtime ( int fd
, usec_t usec
) {
214 if ( IN_SET ( usec
, 0 , USEC_INFINITY
))
215 usec
= now ( CLOCK_REALTIME
);
217 le
= htole64 (( uint64_t ) usec
);
218 if ( fsetxattr ( fd
, "user.crtime_usec" , & le
, sizeof ( le
), 0 ) < 0 )