Compare commits
4 Commits
0419dae715
...
f77d6ec953
Author | SHA1 | Date |
---|---|---|
Zbigniew Jędrzejewski-Szmek | f77d6ec953 | |
Lennart Poettering | df2f58176d | |
Lennart Poettering | ddb439b8f9 | |
Lennart Poettering | cd1361e203 |
|
@ -54,24 +54,19 @@
|
||||||
(i.e. <constant>SD_LISTEN_FDS_START</constant>), the remaining
|
(i.e. <constant>SD_LISTEN_FDS_START</constant>), the remaining
|
||||||
descriptors follow at 4, 5, 6, …, if any.</para>
|
descriptors follow at 4, 5, 6, …, if any.</para>
|
||||||
|
|
||||||
<para>If a daemon receives more than one file descriptor, they
|
<para>If a daemon receives more than one file descriptor, they will be passed in the same order as
|
||||||
will be passed in the same order as configured in the systemd
|
configured in the systemd socket unit file (see
|
||||||
socket unit file (see
|
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
details) — if there's only one such file (see below). Nonetheless, it is recommended to verify the
|
||||||
for details). Nonetheless, it is recommended to verify the correct
|
correct socket types before using them. To simplify this checking, the functions
|
||||||
socket types before using them. To simplify this checking, the
|
|
||||||
functions
|
|
||||||
<citerefentry><refentrytitle>sd_is_fifo</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>sd_is_fifo</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>sd_is_socket</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>sd_is_socket</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>sd_is_socket_inet</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>sd_is_socket_inet</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>sd_is_socket_unix</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>sd_is_socket_unix</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
|
||||||
are provided. In order to maximize flexibility, it is recommended
|
provided. In order to maximize flexibility, it is recommended to make these checks as loose as possible
|
||||||
to make these checks as loose as possible without allowing
|
without allowing incorrect setups. i.e. often, the actual port number a socket is bound to matters little
|
||||||
incorrect setups. i.e. often, the actual port number a socket is
|
for the service to work, hence it should not be verified. On the other hand, whether a socket is a
|
||||||
bound to matters little for the service to work, hence it should
|
datagram or stream socket matters a lot for the most common program logics and should be checked.</para>
|
||||||
not be verified. On the other hand, whether a socket is a datagram
|
|
||||||
or stream socket matters a lot for the most common program logics
|
|
||||||
and should be checked.</para>
|
|
||||||
|
|
||||||
<para>This function call will set the FD_CLOEXEC flag for all
|
<para>This function call will set the FD_CLOEXEC flag for all
|
||||||
passed file descriptors to avoid further inheritance to children
|
passed file descriptors to avoid further inheritance to children
|
||||||
|
|
|
@ -1028,20 +1028,24 @@
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>FileDescriptorStoreMax=</varname></term>
|
<term><varname>FileDescriptorStoreMax=</varname></term>
|
||||||
<listitem><para>Configure how many file descriptors may be stored in the service manager for the service using
|
<listitem><para>Configure how many file descriptors may be stored in the service manager for the
|
||||||
|
service using
|
||||||
<citerefentry><refentrytitle>sd_pid_notify_with_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>'s
|
<citerefentry><refentrytitle>sd_pid_notify_with_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>'s
|
||||||
<literal>FDSTORE=1</literal> messages. This is useful for implementing services that can restart after an
|
<literal>FDSTORE=1</literal> messages. This is useful for implementing services that can restart
|
||||||
explicit request or a crash without losing state. Any open sockets and other file descriptors which should not
|
after an explicit request or a crash without losing state. Any open sockets and other file
|
||||||
be closed during the restart may be stored this way. Application state can either be serialized to a file in
|
descriptors which should not be closed during the restart may be stored this way. Application state
|
||||||
<filename>/run</filename>, or better, stored in a
|
can either be serialized to a file in <filename>/run</filename>, or better, stored in a
|
||||||
<citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry> memory file
|
<citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||||
descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service manager. All file descriptors
|
memory file descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service
|
||||||
passed to the service manager from a specific service are passed back to the service's main process on the next
|
manager. All file descriptors passed to the service manager from a specific service are passed back
|
||||||
service restart. Any file descriptors passed to the service manager are automatically closed when
|
to the service's main process on the next service restart (see
|
||||||
<constant>POLLHUP</constant> or <constant>POLLERR</constant> is seen on them, or when the service is fully
|
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
|
||||||
stopped and no job is queued or being executed for it. If this option is used, <varname>NotifyAccess=</varname>
|
details about the precise protocol used and the order in which the file descriptors are passed). Any
|
||||||
(see above) should be set to open access to the notification socket provided by systemd. If
|
file descriptors passed to the service manager are automatically closed when
|
||||||
<varname>NotifyAccess=</varname> is not set, it will be implicitly set to
|
<constant>POLLHUP</constant> or <constant>POLLERR</constant> is seen on them, or when the service is
|
||||||
|
fully stopped and no job is queued or being executed for it. If this option is used,
|
||||||
|
<varname>NotifyAccess=</varname> (see above) should be set to open access to the notification socket
|
||||||
|
provided by systemd. If <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
|
||||||
<option>main</option>.</para></listitem>
|
<option>main</option>.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
|
@ -81,16 +81,14 @@
|
||||||
services, as well as parallelized starting of services. See the
|
services, as well as parallelized starting of services. See the
|
||||||
blog stories linked at the end for an introduction.</para>
|
blog stories linked at the end for an introduction.</para>
|
||||||
|
|
||||||
<para>Note that the daemon software configured for socket
|
<para>Note that the daemon software configured for socket activation with socket units needs to be able
|
||||||
activation with socket units needs to be able to accept sockets
|
to accept sockets from systemd, either via systemd's native socket passing interface (see
|
||||||
from systemd, either via systemd's native socket passing interface
|
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
|
||||||
(see
|
details about the precise protocol used and the order in which the file descriptors are passed) or via
|
||||||
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
traditional <citerefentry
|
||||||
for details) or via the traditional
|
project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>-style
|
||||||
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>-style
|
socket passing (i.e. sockets passed in via standard input and output, using
|
||||||
socket passing (i.e. sockets passed in via standard input and
|
<varname>StandardInput=socket</varname> in the service file).</para>
|
||||||
output, using <varname>StandardInput=socket</varname> in the
|
|
||||||
service file).</para>
|
|
||||||
|
|
||||||
<para>All network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
|
<para>All network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
|
||||||
namespace (see <citerefentry
|
namespace (see <citerefentry
|
||||||
|
|
|
@ -50,13 +50,48 @@ struct statx STATX_DEFINITION;
|
||||||
struct new_statx STATX_DEFINITION;
|
struct new_statx STATX_DEFINITION;
|
||||||
|
|
||||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
#ifndef STATX_BTIME
|
#ifndef AT_STATX_DONT_SYNC
|
||||||
#define STATX_BTIME 0x00000800U
|
#define AT_STATX_DONT_SYNC 0x4000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
#ifndef AT_STATX_DONT_SYNC
|
#ifndef STATX_TYPE
|
||||||
#define AT_STATX_DONT_SYNC 0x4000
|
#define STATX_TYPE 0x00000001U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_MODE
|
||||||
|
#define STATX_MODE 0x00000002U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_UID
|
||||||
|
#define STATX_UID 0x00000008U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_ATIME
|
||||||
|
#define STATX_ATIME 0x00000020U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_MTIME
|
||||||
|
#define STATX_MTIME 0x00000040U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_CTIME
|
||||||
|
#define STATX_CTIME 0x00000080U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_INO
|
||||||
|
#define STATX_INO 0x00000100U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||||
|
#ifndef STATX_BTIME
|
||||||
|
#define STATX_BTIME 0x00000800U
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
|
/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
|
||||||
|
|
|
@ -244,12 +244,28 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) {
|
||||||
if (u == USEC_INFINITY ||
|
if (u == USEC_INFINITY ||
|
||||||
u / USEC_PER_SEC >= TIME_T_MAX) {
|
u / USEC_PER_SEC >= TIME_T_MAX) {
|
||||||
ts->tv_sec = (time_t) -1;
|
ts->tv_sec = (time_t) -1;
|
||||||
ts->tv_nsec = (long) -1;
|
ts->tv_nsec = -1L;
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
ts->tv_sec = (time_t) (u / USEC_PER_SEC);
|
ts->tv_sec = (time_t) (u / USEC_PER_SEC);
|
||||||
ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
|
ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
|
||||||
|
assert(ts);
|
||||||
|
|
||||||
|
if (n == NSEC_INFINITY ||
|
||||||
|
n / NSEC_PER_SEC >= TIME_T_MAX) {
|
||||||
|
ts->tv_sec = (time_t) -1;
|
||||||
|
ts->tv_nsec = -1L;
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
|
||||||
|
ts->tv_nsec = (long) (n % NSEC_PER_SEC);
|
||||||
|
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
|
||||||
usec_t timespec_load(const struct timespec *ts) _pure_;
|
usec_t timespec_load(const struct timespec *ts) _pure_;
|
||||||
nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
|
nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
|
||||||
struct timespec *timespec_store(struct timespec *ts, usec_t u);
|
struct timespec *timespec_store(struct timespec *ts, usec_t u);
|
||||||
|
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n);
|
||||||
|
|
||||||
usec_t timeval_load(const struct timeval *tv) _pure_;
|
usec_t timeval_load(const struct timeval *tv) _pure_;
|
||||||
struct timeval *timeval_store(struct timeval *tv, usec_t u);
|
struct timeval *timeval_store(struct timeval *tv, usec_t u);
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
|
#include "missing_stat.h"
|
||||||
|
#include "missing_syscall.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
#include "mountpoint-util.h"
|
#include "mountpoint-util.h"
|
||||||
|
@ -490,29 +492,109 @@ static DIR* opendir_nomod(const char *path) {
|
||||||
return xopendirat_nomod(AT_FDCWD, path);
|
return xopendirat_nomod(AT_FDCWD, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline nsec_t load_statx_timestamp_nsec(const struct statx_timestamp *ts) {
|
||||||
|
assert(ts);
|
||||||
|
|
||||||
|
if (ts->tv_sec < 0)
|
||||||
|
return NSEC_INFINITY;
|
||||||
|
|
||||||
|
if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
|
||||||
|
return NSEC_INFINITY;
|
||||||
|
|
||||||
|
return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
static int dir_cleanup(
|
static int dir_cleanup(
|
||||||
Item *i,
|
Item *i,
|
||||||
const char *p,
|
const char *p,
|
||||||
DIR *d,
|
DIR *d,
|
||||||
const struct stat *ds,
|
nsec_t self_atime_nsec,
|
||||||
usec_t cutoff,
|
nsec_t self_mtime_nsec,
|
||||||
dev_t rootdev,
|
nsec_t cutoff_nsec,
|
||||||
|
dev_t rootdev_major,
|
||||||
|
dev_t rootdev_minor,
|
||||||
bool mountpoint,
|
bool mountpoint,
|
||||||
int maxdepth,
|
int maxdepth,
|
||||||
bool keep_this_level) {
|
bool keep_this_level) {
|
||||||
|
|
||||||
struct dirent *dent;
|
static bool use_statx = true;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
|
struct dirent *dent;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
FOREACH_DIRENT_ALL(dent, d, break) {
|
FOREACH_DIRENT_ALL(dent, d, break) {
|
||||||
struct stat s;
|
|
||||||
usec_t age;
|
|
||||||
_cleanup_free_ char *sub_path = NULL;
|
_cleanup_free_ char *sub_path = NULL;
|
||||||
|
nsec_t atime_nsec, mtime_nsec, ctime_nsec, btime_nsec;
|
||||||
|
mode_t mode;
|
||||||
|
uid_t uid;
|
||||||
|
|
||||||
if (dot_or_dot_dot(dent->d_name))
|
if (dot_or_dot_dot(dent->d_name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (use_statx) {
|
||||||
|
/* If statx() is supported, use it. It's preferable over fstatat() since it tells us
|
||||||
|
* explicitly where we are looking at a mount point, for free as side
|
||||||
|
* information. Determing the same information without statx() is hard, see the
|
||||||
|
* complexity of path_is_mount_point(), and also much slower as it requires a numbre
|
||||||
|
* of syscalls instead of just one. Hence, when we have modern statx() we use it
|
||||||
|
* instead of fstat() and do proper mount point checks, while on older kernels's well
|
||||||
|
* do traditional st_dev based detection of mount points.
|
||||||
|
*
|
||||||
|
* Using statx() for detecting mount points also has the benfit that we handle weird
|
||||||
|
* file systems such as overlayfs better where each file is originating from a
|
||||||
|
* different st_dev. */
|
||||||
|
|
||||||
|
struct statx sx
|
||||||
|
#if HAS_FEATURE_MEMORY_SANITIZER
|
||||||
|
= {}
|
||||||
|
# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
if (statx(dirfd(d), dent->d_name,
|
||||||
|
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
|
||||||
|
STATX_TYPE|STATX_MODE|STATX_UID|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
|
||||||
|
&sx) < 0) {
|
||||||
|
|
||||||
|
if (errno == ENOENT)
|
||||||
|
continue;
|
||||||
|
if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EPERM)
|
||||||
|
use_statx = false; /* Not supported or blocked by seccomp or so */
|
||||||
|
else {
|
||||||
|
/* FUSE, NFS mounts, SELinux might return EACCES */
|
||||||
|
r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
|
||||||
|
"statx(%s/%s) failed: %m", p, dent->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) {
|
||||||
|
/* Yay, we have the mount point API, use it */
|
||||||
|
if (FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT)) {
|
||||||
|
log_debug("Ignoring \"%s/%s\": different mount points.", p, dent->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* So we have statx() but the STATX_ATTR_MOUNT_ROOT flag is not
|
||||||
|
* supported, fall back to traditional stx_dev checking. */
|
||||||
|
if (sx.stx_dev_major != rootdev_major ||
|
||||||
|
sx.stx_dev_minor != rootdev_minor) {
|
||||||
|
log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = sx.stx_mode;
|
||||||
|
uid = sx.stx_uid;
|
||||||
|
atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? load_statx_timestamp_nsec(&sx.stx_atime) : 0;
|
||||||
|
mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? load_statx_timestamp_nsec(&sx.stx_mtime) : 0;
|
||||||
|
ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? load_statx_timestamp_nsec(&sx.stx_ctime) : 0;
|
||||||
|
btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? load_statx_timestamp_nsec(&sx.stx_btime) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!use_statx) {
|
||||||
|
struct stat s;
|
||||||
|
|
||||||
if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
|
if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
continue;
|
continue;
|
||||||
|
@ -524,15 +606,23 @@ static int dir_cleanup(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stay on the same filesystem */
|
/* Stay on the same filesystem */
|
||||||
if (s.st_dev != rootdev) {
|
if (major(s.st_dev) != rootdev_major || minor(s.st_dev) != rootdev_minor) {
|
||||||
log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
|
log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mode = s.st_mode;
|
||||||
|
uid = s.st_uid;
|
||||||
|
atime_nsec = timespec_load_nsec(&s.st_atim);
|
||||||
|
mtime_nsec = timespec_load_nsec(&s.st_mtim);
|
||||||
|
ctime_nsec = timespec_load_nsec(&s.st_ctim);
|
||||||
|
btime_nsec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to detect bind mounts of the same filesystem instance; they
|
/* Try to detect bind mounts of the same filesystem instance; they
|
||||||
* do not differ in device major/minors. This type of query is not
|
* do not differ in device major/minors. This type of query is not
|
||||||
* supported on all kernels or filesystem types though. */
|
* supported on all kernels or filesystem types though. */
|
||||||
if (S_ISDIR(s.st_mode)) {
|
if (S_ISDIR(mode)) {
|
||||||
int q;
|
int q;
|
||||||
|
|
||||||
q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
|
q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
|
||||||
|
@ -561,12 +651,12 @@ static int dir_cleanup(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S_ISDIR(s.st_mode)) {
|
if (S_ISDIR(mode)) {
|
||||||
_cleanup_closedir_ DIR *sub_dir = NULL;
|
_cleanup_closedir_ DIR *sub_dir = NULL;
|
||||||
|
|
||||||
if (mountpoint &&
|
if (mountpoint &&
|
||||||
streq(dent->d_name, "lost+found") &&
|
streq(dent->d_name, "lost+found") &&
|
||||||
s.st_uid == 0) {
|
uid == 0) {
|
||||||
log_debug("Ignoring directory \"%s\".", sub_path);
|
log_debug("Ignoring directory \"%s\".", sub_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -589,7 +679,11 @@ static int dir_cleanup(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
|
q = dir_cleanup(i,
|
||||||
|
sub_path, sub_dir,
|
||||||
|
atime_nsec, mtime_nsec, cutoff_nsec,
|
||||||
|
rootdev_major, rootdev_minor,
|
||||||
|
false, maxdepth-1, false);
|
||||||
if (q < 0)
|
if (q < 0)
|
||||||
r = q;
|
r = q;
|
||||||
}
|
}
|
||||||
|
@ -605,22 +699,28 @@ static int dir_cleanup(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore ctime, we change it when deleting */
|
/* Ignore ctime, we change it when deleting */
|
||||||
age = timespec_load(&s.st_mtim);
|
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
||||||
if (age >= cutoff) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
/* Follows spelling in stat(1). */
|
/* Follows spelling in stat(1). */
|
||||||
log_debug("Directory \"%s\": modify time %s is too new.",
|
log_debug("Directory \"%s\": modify time %s is too new.",
|
||||||
sub_path,
|
sub_path,
|
||||||
format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
|
format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
age = timespec_load(&s.st_atim);
|
if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
|
||||||
if (age >= cutoff) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
log_debug("Directory \"%s\": access time %s is too new.",
|
log_debug("Directory \"%s\": access time %s is too new.",
|
||||||
sub_path,
|
sub_path,
|
||||||
format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
|
format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
log_debug("Directory \"%s\": birth time %s is too new.",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,14 +732,14 @@ static int dir_cleanup(
|
||||||
} else {
|
} else {
|
||||||
/* Skip files for which the sticky bit is set. These are semantics we define, and are
|
/* 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. */
|
* unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */
|
||||||
if (s.st_mode & S_ISVTX) {
|
if (mode & S_ISVTX) {
|
||||||
log_debug("Skipping \"%s\": sticky bit set.", sub_path);
|
log_debug("Skipping \"%s\": sticky bit set.", sub_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mountpoint &&
|
if (mountpoint &&
|
||||||
S_ISREG(s.st_mode) &&
|
S_ISREG(mode) &&
|
||||||
s.st_uid == 0 &&
|
uid == 0 &&
|
||||||
STR_IN_SET(dent->d_name,
|
STR_IN_SET(dent->d_name,
|
||||||
".journal",
|
".journal",
|
||||||
"aquota.user",
|
"aquota.user",
|
||||||
|
@ -649,13 +749,13 @@ static int dir_cleanup(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore sockets that are listed in /proc/net/unix */
|
/* Ignore sockets that are listed in /proc/net/unix */
|
||||||
if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
|
if (S_ISSOCK(mode) && unix_socket_alive(sub_path)) {
|
||||||
log_debug("Skipping \"%s\": live socket.", sub_path);
|
log_debug("Skipping \"%s\": live socket.", sub_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore device nodes */
|
/* Ignore device nodes */
|
||||||
if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
|
if (S_ISCHR(mode) || S_ISBLK(mode)) {
|
||||||
log_debug("Skipping \"%s\": a device.", sub_path);
|
log_debug("Skipping \"%s\": a device.", sub_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -666,31 +766,36 @@ static int dir_cleanup(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
age = timespec_load(&s.st_mtim);
|
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
||||||
if (age >= cutoff) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
/* Follows spelling in stat(1). */
|
/* Follows spelling in stat(1). */
|
||||||
log_debug("File \"%s\": modify time %s is too new.",
|
log_debug("File \"%s\": modify time %s is too new.",
|
||||||
sub_path,
|
sub_path,
|
||||||
format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
|
format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
age = timespec_load(&s.st_atim);
|
if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
|
||||||
if (age >= cutoff) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
log_debug("File \"%s\": access time %s is too new.",
|
log_debug("File \"%s\": access time %s is too new.",
|
||||||
sub_path,
|
sub_path,
|
||||||
format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
|
format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
age = timespec_load(&s.st_ctim);
|
if (ctime_nsec != NSEC_INFINITY && ctime_nsec >= cutoff_nsec) {
|
||||||
if (age >= cutoff) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
log_debug("File \"%s\": change time %s is too new.",
|
log_debug("File \"%s\": change time %s is too new.",
|
||||||
sub_path,
|
sub_path,
|
||||||
format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
|
format_timestamp_style(a, sizeof(a), ctime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
log_debug("File \"%s\": birth time %s is too new.",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,21 +810,19 @@ static int dir_cleanup(
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
|
char a[FORMAT_TIMESTAMP_MAX], m[FORMAT_TIMESTAMP_MAX];
|
||||||
usec_t age1, age2;
|
struct timespec ts[2];
|
||||||
|
|
||||||
age1 = timespec_load(&ds->st_atim);
|
|
||||||
age2 = timespec_load(&ds->st_mtim);
|
|
||||||
|
|
||||||
log_debug("Restoring access and modification time on \"%s\": %s, %s",
|
log_debug("Restoring access and modification time on \"%s\": %s, %s",
|
||||||
p,
|
p,
|
||||||
format_timestamp_style(a, sizeof(a), age1, TIMESTAMP_US),
|
format_timestamp_style(a, sizeof(a), self_atime_nsec / NSEC_PER_USEC, TIMESTAMP_US),
|
||||||
format_timestamp_style(b, sizeof(b), age2, TIMESTAMP_US));
|
format_timestamp_style(m, sizeof(m), self_mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
|
||||||
|
timespec_store_nsec(ts + 0, self_atime_nsec);
|
||||||
|
timespec_store_nsec(ts + 1, self_mtime_nsec);
|
||||||
|
|
||||||
/* Restore original directory timestamps */
|
/* Restore original directory timestamps */
|
||||||
if (futimens(dirfd(d), (struct timespec[]) {
|
if (futimens(dirfd(d), ts) < 0)
|
||||||
ds->st_atim,
|
|
||||||
ds->st_mtim }) < 0)
|
|
||||||
log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
|
log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2186,11 +2289,20 @@ static int remove_item(Item *i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clean_item_instance(Item *i, const char* instance) {
|
static int clean_item_instance(Item *i, const char* instance) {
|
||||||
_cleanup_closedir_ DIR *d = NULL;
|
|
||||||
struct stat s, ps;
|
|
||||||
bool mountpoint;
|
|
||||||
usec_t cutoff, n;
|
|
||||||
char timestamp[FORMAT_TIMESTAMP_MAX];
|
char timestamp[FORMAT_TIMESTAMP_MAX];
|
||||||
|
_cleanup_closedir_ DIR *d = NULL;
|
||||||
|
uint32_t dev_major, dev_minor;
|
||||||
|
nsec_t atime_nsec, mtime_nsec;
|
||||||
|
int mountpoint = -1;
|
||||||
|
usec_t cutoff, n;
|
||||||
|
uint64_t ino;
|
||||||
|
|
||||||
|
struct statx sx
|
||||||
|
#if HAS_FEATURE_MEMORY_SANITIZER
|
||||||
|
= {}
|
||||||
|
# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
|
@ -2213,24 +2325,53 @@ static int clean_item_instance(Item *i, const char* instance) {
|
||||||
return log_error_errno(errno, "Failed to open directory %s: %m", instance);
|
return log_error_errno(errno, "Failed to open directory %s: %m", instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statx(dirfd(d), "", AT_EMPTY_PATH, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, &sx) < 0) {
|
||||||
|
struct stat s;
|
||||||
|
|
||||||
|
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||||
|
return log_error_errno(errno, "statx(%s) failed: %m", i->path);
|
||||||
|
|
||||||
if (fstat(dirfd(d), &s) < 0)
|
if (fstat(dirfd(d), &s) < 0)
|
||||||
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
|
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
|
||||||
|
|
||||||
if (!S_ISDIR(s.st_mode))
|
dev_major = major(s.st_dev);
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
|
dev_minor = minor(s.st_dev);
|
||||||
"%s is not a directory.", i->path);
|
ino = s.st_ino;
|
||||||
|
atime_nsec = timespec_load_nsec(&s.st_atim);
|
||||||
|
mtime_nsec = timespec_load_nsec(&s.st_mtim);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
|
||||||
|
mountpoint = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
|
||||||
|
|
||||||
|
dev_major = sx.stx_dev_major;
|
||||||
|
dev_minor = sx.stx_dev_minor;
|
||||||
|
ino = sx.stx_ino;
|
||||||
|
atime_nsec = load_statx_timestamp_nsec(&sx.stx_atime);
|
||||||
|
mtime_nsec = load_statx_timestamp_nsec(&sx.stx_mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mountpoint < 0) {
|
||||||
|
struct stat ps;
|
||||||
|
|
||||||
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
|
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
|
||||||
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
|
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
|
||||||
|
|
||||||
mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
|
mountpoint =
|
||||||
|
dev_major != major(ps.st_dev) ||
|
||||||
|
dev_minor != minor(ps.st_dev) ||
|
||||||
|
ino != ps.st_ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
log_debug("Cleanup threshold for %s \"%s\" is %s",
|
log_debug("Cleanup threshold for %s \"%s\" is %s",
|
||||||
mountpoint ? "mount point" : "directory",
|
mountpoint ? "mount point" : "directory",
|
||||||
instance,
|
instance,
|
||||||
format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
|
format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
|
||||||
|
|
||||||
return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
|
return dir_cleanup(i, instance, d,
|
||||||
|
atime_nsec, mtime_nsec, cutoff * NSEC_PER_USEC,
|
||||||
|
dev_major, dev_minor, mountpoint,
|
||||||
MAX_DEPTH, i->keep_first_level);
|
MAX_DEPTH, i->keep_first_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue