1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/magic.h>
9 #include "bus-locator.h"
10 #include "devnum-util.h"
11 #include "errno-util.h"
13 #include "format-util.h"
15 #include "label-util.h"
16 #include "limits-util.h"
18 #include "main-func.h"
19 #include "mkdir-label.h"
20 #include "mount-util.h"
21 #include "mountpoint-util.h"
22 #include "path-util.h"
23 #include "quota-util.h"
26 #include "smack-util.h"
27 #include "stat-util.h"
28 #include "stdio-util.h"
29 #include "string-util.h"
31 #include "user-util.h"
34 static int acquire_runtime_dir_properties(uint64_t *ret_size
, uint64_t *ret_inodes
) {
35 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
36 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
37 uint64_t size
, inodes
;
43 r
= sd_bus_default_system(&bus
);
45 return log_error_errno(r
, "Failed to connect to system bus: %m");
47 r
= bus_get_property_trivial(bus
, bus_login_mgr
, "RuntimeDirectorySize", &error
, 't', &size
);
49 log_warning_errno(r
, "Failed to acquire runtime directory size, ignoring: %s", bus_error_message(&error
, r
));
50 sd_bus_error_free(&error
);
52 size
= physical_memory_scale(10U, 100U); /* 10% */
55 r
= bus_get_property_trivial(bus
, bus_login_mgr
, "RuntimeDirectoryInodesMax", &error
, 't', &inodes
);
57 log_warning_errno(r
, "Failed to acquire number of inodes for runtime directory, ignoring: %s", bus_error_message(&error
, r
));
58 sd_bus_error_free(&error
);
60 inodes
= DIV_ROUND_UP(size
, 4096);
69 static int user_mkdir_runtime_path(
70 const char *runtime_path
,
73 uint64_t runtime_dir_size
,
74 uint64_t runtime_dir_inodes
) {
79 assert(path_is_absolute(runtime_path
));
80 assert(uid_is_valid(uid
));
81 assert(gid_is_valid(gid
));
83 r
= mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE
);
85 return log_error_errno(r
, "Failed to create /run/user: %m");
87 if (path_is_mount_point(runtime_path
) > 0)
88 log_debug("%s is already a mount point", runtime_path
);
90 char options
[STRLEN("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*")
91 + DECIMAL_STR_MAX(uid_t
)
92 + DECIMAL_STR_MAX(gid_t
)
93 + DECIMAL_STR_MAX(uint64_t)
94 + DECIMAL_STR_MAX(uint64_t)];
97 "mode=0700,uid=" UID_FMT
",gid=" GID_FMT
",size=%" PRIu64
",nr_inodes=%" PRIu64
"%s",
98 uid
, gid
, runtime_dir_size
, runtime_dir_inodes
,
99 mac_smack_use() ? ",smackfsroot=*" : "");
101 _cleanup_free_
char *d
= strdup(runtime_path
);
105 r
= mkdir_label(runtime_path
, 0700);
106 if (r
< 0 && r
!= -EEXIST
)
107 return log_error_errno(r
, "Failed to create %s: %m", runtime_path
);
109 _cleanup_(rmdir_and_freep
) char *destroy
= TAKE_PTR(d
); /* auto-destroy */
111 r
= mount_nofollow_verbose(LOG_DEBUG
, "tmpfs", runtime_path
, "tmpfs", MS_NODEV
|MS_NOSUID
, options
);
113 if (!ERRNO_IS_PRIVILEGE(r
))
114 return log_error_errno(r
, "Failed to mount per-user tmpfs directory %s: %m", runtime_path
);
117 "Failed to mount per-user tmpfs directory %s.\n"
118 "Assuming containerized execution, ignoring: %m", runtime_path
);
120 r
= chmod_and_chown(runtime_path
, 0700, uid
, gid
);
122 return log_error_errno(r
, "Failed to change ownership and mode of \"%s\": %m", runtime_path
);
125 destroy
= mfree(destroy
); /* deactivate auto-destroy */
127 r
= label_fix(runtime_path
, 0);
129 log_warning_errno(r
, "Failed to fix label of \"%s\", ignoring: %m", runtime_path
);
135 static int do_mount(UserRecord
*ur
) {
140 if (!uid_is_valid(ur
->uid
) || !gid_is_valid(user_record_gid(ur
)))
141 return log_error_errno(SYNTHETIC_ERRNO(ENOMSG
), "User '%s' lacks UID or GID, refusing.", ur
->user_name
);
143 uint64_t runtime_dir_size
, runtime_dir_inodes
;
144 r
= acquire_runtime_dir_properties(&runtime_dir_size
, &runtime_dir_inodes
);
148 char runtime_path
[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t
)];
149 xsprintf(runtime_path
, "/run/user/" UID_FMT
, ur
->uid
);
151 log_debug("Will mount %s owned by "UID_FMT
":"GID_FMT
, runtime_path
, ur
->uid
, user_record_gid(ur
));
152 return user_mkdir_runtime_path(runtime_path
, ur
->uid
, user_record_gid(ur
), runtime_dir_size
, runtime_dir_inodes
);
155 static int user_remove_runtime_path(const char *runtime_path
) {
158 assert(runtime_path
);
159 assert(path_is_absolute(runtime_path
));
161 r
= rm_rf(runtime_path
, 0);
163 log_debug_errno(r
, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path
);
165 /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to
167 r
= RET_NERRNO(umount2(runtime_path
, MNT_DETACH
));
168 if (r
< 0 && !IN_SET(r
, -EINVAL
, -ENOENT
))
169 log_debug_errno(r
, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path
);
171 r
= rm_rf(runtime_path
, REMOVE_ROOT
);
172 if (r
< 0 && r
!= -ENOENT
)
173 return log_error_errno(r
, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path
);
178 static int do_umount(const char *user
) {
179 char runtime_path
[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t
)];
183 /* The user may be already removed. So, first try to parse the string by parse_uid(),
184 * and if it fails, fall back to get_user_creds(). */
185 if (parse_uid(user
, &uid
) < 0) {
186 r
= get_user_creds(&user
, &uid
, NULL
, NULL
, NULL
, 0);
188 return log_error_errno(r
,
189 r
== -ESRCH
? "No such user \"%s\"" :
190 r
== -ENOMSG
? "UID \"%s\" is invalid or has an invalid main group"
191 : "Failed to look up user \"%s\": %m",
195 xsprintf(runtime_path
, "/run/user/" UID_FMT
, uid
);
197 log_debug("Will remove %s", runtime_path
);
198 return user_remove_runtime_path(runtime_path
);
201 static int apply_tmpfs_quota(
207 _cleanup_set_free_ Set
*processed
= NULL
;
210 assert(uid_is_valid(uid
));
212 STRV_FOREACH(p
, paths
) {
213 if (limit
== UINT64_MAX
&& scale
== UINT32_MAX
) {
214 log_debug("No disk quota on '%s' is requested.", *p
);
218 _cleanup_close_
int fd
= open(*p
, O_DIRECTORY
|O_CLOEXEC
);
220 log_warning_errno(errno
, "Failed to open '%s' in order to set quota, ignoring: %m", *p
);
225 if (fstat(fd
, &st
) < 0) {
226 log_warning_errno(errno
, "Failed to stat '%s' in order to set quota, ignoring: %m", *p
);
230 /* Cover for bind mounted or symlinked /var/tmp/ + /tmp/ */
231 if (set_contains(processed
, DEVNUM_TO_PTR(st
.st_dev
))) {
232 log_debug("Not setting quota on '%s', since already processed.", *p
);
236 /* Remember we already dealt with this fs, even if the subsequent operation fails, since
237 * there's no point in appyling quota twice, regardless if it succeeds or not. */
238 if (set_ensure_put(&processed
, /* hash_ops= */ NULL
, DEVNUM_TO_PTR(st
.st_dev
)) < 0)
242 if (fstatfs(fd
, &sfs
) < 0) {
243 log_warning_errno(errno
, "Failed to statfs '%s' in order to set quota, ignoring: %m", *p
);
247 if (!is_fs_type(&sfs
, TMPFS_MAGIC
)) {
248 log_debug("Not setting quota on '%s', since not tmpfs.", *p
);
253 r
= RET_NERRNO(quotactl_fd(fd
, QCMD_FIXED(Q_GETQUOTA
, USRQUOTA
), uid
, &req
));
256 else if (ERRNO_IS_NEG_NOT_SUPPORTED(r
)) {
257 log_debug_errno(r
, "No UID quota support on %s, not setting quota: %m", *p
);
259 } else if (ERRNO_IS_NEG_PRIVILEGE(r
)) {
260 log_debug_errno(r
, "Lacking privileges to query UID quota on %s, not setting quota: %m", *p
);
263 log_warning_errno(r
, "Failed to query disk quota on %s for UID " UID_FMT
", ignoring: %m", *p
, uid
);
269 (scale
== UINT32_MAX
) ? UINT64_MAX
:
270 (uint64_t) ((double) (sfs
.f_blocks
* sfs
.f_frsize
) * scale
/ UINT32_MAX
);
275 if (FLAGS_SET(req
.dqb_valid
, QIF_BLIMITS
) && v
== req
.dqb_bhardlimit
) {
276 /* Shortcut things if everything is set up properly already */
277 log_debug("Configured quota on '%s' already matches the intended setting, not updating quota.", *p
);
281 req
.dqb_valid
= QIF_BLIMITS
;
282 req
.dqb_bsoftlimit
= req
.dqb_bhardlimit
= v
;
284 r
= RET_NERRNO(quotactl_fd(fd
, QCMD_FIXED(Q_SETQUOTA
, USRQUOTA
), uid
, &req
));
286 log_debug_errno(r
, "Not setting UID quota on %s since UID quota is not supported: %m", *p
);
288 } else if (ERRNO_IS_NEG_PRIVILEGE(r
)) {
289 log_debug_errno(r
, "Lacking privileges to set UID quota on %s, skipping: %m", *p
);
292 log_warning_errno(r
, "Failed to set disk quota limit to %s on %s for UID " UID_FMT
", ignoring: %m",
293 FORMAT_BYTES(v
* QIF_DQBLKSIZE
), *p
, uid
);
297 log_info("Successfully configured disk quota for UID " UID_FMT
" on %s to %s", uid
, *p
, FORMAT_BYTES(v
* QIF_DQBLKSIZE
));
303 static int do_tmpfs_quota(UserRecord
*ur
) {
308 if (user_record_is_root(ur
)) {
309 log_debug("Not applying tmpfs quota to root user.");
313 if (!uid_is_valid(ur
->uid
))
314 return log_error_errno(SYNTHETIC_ERRNO(ENOMSG
), "User '%s' lacks UID, refusing.", ur
->user_name
);
316 r
= apply_tmpfs_quota(STRV_MAKE("/tmp", "/var/tmp"), ur
->uid
, ur
->tmp_limit
.limit
, user_record_tmp_limit_scale(ur
));
320 r
= apply_tmpfs_quota(STRV_MAKE("/dev/shm"), ur
->uid
, ur
->dev_shm_limit
.limit
, user_record_dev_shm_limit_scale(ur
));
327 static int run(int argc
, char *argv
[]) {
333 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
334 "This program takes two arguments.");
336 const char *verb
= argv
[1], *user
= argv
[2];
338 if (!STR_IN_SET(verb
, "start", "stop"))
339 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
340 "First argument must be either \"start\" or \"stop\".");
348 if (streq(verb
, "start")) {
349 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
350 r
= userdb_by_name(user
, /* match= */ NULL
, USERDB_PARSE_NUMERIC
|USERDB_SUPPRESS_SHADOW
, &ur
);
352 return log_error_errno(r
, "User '%s' does not exist: %m", user
);
354 return log_error_errno(r
, "Failed to resolve user '%s': %m", user
);
356 /* We do two things here: mount the per-user XDG_RUNTIME_DIR, and set up tmpfs quota on /tmp/
360 RET_GATHER(r
, do_mount(ur
));
361 RET_GATHER(r
, do_tmpfs_quota(ur
));
365 if (streq(verb
, "stop"))
366 return do_umount(user
);
368 assert_not_reached();
371 DEFINE_MAIN_FUNCTION(run
);