#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "home-util.h"
#include "homed-home-bus.h"
#include "homed-home.h"
+#include "missing_magic.h"
#include "missing_syscall.h"
#include "mkdir.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-table.h"
#include "strv.h"
+#include "user-record-pwquality.h"
#include "user-record-sign.h"
#include "user-record-util.h"
-#include "user-record-pwquality.h"
#include "user-record.h"
#include "user-util.h"
uint64_t *ret_disk_usage,
uint64_t *ret_disk_free,
uint64_t *ret_disk_ceiling,
- uint64_t *ret_disk_floor) {
+ uint64_t *ret_disk_floor,
+ statfs_f_type_t *ret_fstype,
+ mode_t *ret_access_mode) {
uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX,
stat_used = UINT64_MAX, fs_size = UINT64_MAX, header_size = 0;
-
+ mode_t access_mode = MODE_INVALID;
+ statfs_f_type_t fstype = 0;
struct statfs sfs;
+ struct stat st;
const char *hd;
int r;
ip = user_record_image_path(h->record);
if (ip) {
- struct stat st;
-
if (stat(ip, &st) < 0)
log_debug_errno(errno, "Failed to stat() %s, ignoring: %m", ip);
else if (S_ISREG(st.st_mode)) {
if (!hd)
goto finish;
+ if (stat(hd, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat() %s, ignoring: %m", hd);
+ goto finish;
+ }
+
+ r = stat_verify_directory(&st);
+ if (r < 0) {
+ log_debug_errno(r, "Home directory %s is not a directory, ignoring: %m", hd);
+ goto finish;
+ }
+
+ access_mode = st.st_mode & 07777;
+
if (statfs(hd, &sfs) < 0) {
log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", hd);
goto finish;
}
+ fstype = sfs.f_type;
+
disk_free = sfs.f_bsize * sfs.f_bavail;
fs_size = sfs.f_bsize * sfs.f_blocks;
if (disk_size != UINT64_MAX && disk_size > fs_size)
*ret_disk_ceiling = disk_ceiling;
if (ret_disk_floor)
*ret_disk_floor = disk_floor;
+ if (ret_fstype)
+ *ret_fstype = fstype;
+ if (ret_access_mode)
+ *ret_access_mode = access_mode;
return 0;
}
uint64_t *ret_disk_usage,
uint64_t *ret_disk_free,
uint64_t *ret_disk_ceiling,
- uint64_t *ret_disk_floor) {
+ uint64_t *ret_disk_floor,
+ statfs_f_type_t *ret_fstype,
+ mode_t *ret_access_mode) {
uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
+ mode_t access_mode = MODE_INVALID;
+ statfs_f_type_t fstype = 0;
struct statfs sfs;
struct dqblk req;
const char *path = NULL;
/* We don't initialize disk_usage from statfs() data here, since the device is likely not used
* by us alone, and disk_usage should only reflect our own use. */
+
+ fstype = sfs.f_type;
}
if (IN_SET(h->record->storage, USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME)) {
*ret_disk_ceiling = disk_ceiling;
if (ret_disk_floor)
*ret_disk_floor = disk_floor;
+ if (ret_fstype)
+ *ret_fstype = fstype;
+ if (ret_access_mode)
+ *ret_access_mode = access_mode;
return 0;
}
uint64_t *ret_disk_usage,
uint64_t *ret_disk_free,
uint64_t *ret_disk_ceiling,
- uint64_t *ret_disk_floor) {
+ uint64_t *ret_disk_floor,
+ statfs_f_type_t *ret_fstype,
+ mode_t *ret_access_mode) {
assert(h);
assert(h->record);
switch (h->record->storage) {
case USER_LUKS:
- return home_get_disk_status_luks(h, state, ret_disk_size, ret_disk_usage, ret_disk_free, ret_disk_ceiling, ret_disk_floor);
+ return home_get_disk_status_luks(h, state, ret_disk_size, ret_disk_usage, ret_disk_free, ret_disk_ceiling, ret_disk_floor, ret_fstype, ret_access_mode);
case USER_CLASSIC:
case USER_DIRECTORY:
case USER_SUBVOLUME:
case USER_FSCRYPT:
case USER_CIFS:
- return home_get_disk_status_directory(h, state, ret_disk_size, ret_disk_usage, ret_disk_free, ret_disk_ceiling, ret_disk_floor);
+ return home_get_disk_status_directory(h, state, ret_disk_size, ret_disk_usage, ret_disk_free, ret_disk_ceiling, ret_disk_floor, ret_fstype, ret_access_mode);
default:
/* don't know */
*ret_disk_ceiling = UINT64_MAX;
if (ret_disk_floor)
*ret_disk_floor = UINT64_MAX;
+ if (ret_fstype)
+ *ret_fstype = 0;
+ if (ret_access_mode)
+ *ret_access_mode = MODE_INVALID;
return 0;
}
uint64_t *ret_disk_usage,
uint64_t *ret_disk_free,
uint64_t *ret_disk_ceiling,
- uint64_t *ret_disk_floor) {
+ uint64_t *ret_disk_floor,
+ statfs_f_type_t *ret_fstype,
+ mode_t *ret_access_mode) {
assert(h);
ret_disk_usage,
ret_disk_free,
ret_disk_ceiling,
- ret_disk_floor);
+ ret_disk_floor,
+ ret_fstype,
+ ret_access_mode);
+}
+
+static const char *fstype_magic_to_name(statfs_f_type_t magic) {
+ /* For now, let's only translate the magic values of the file systems we actually are able to manage */
+
+ if (F_TYPE_EQUAL(magic, EXT4_SUPER_MAGIC))
+ return "ext4";
+ if (F_TYPE_EQUAL(magic, XFS_SUPER_MAGIC))
+ return "xfs";
+ if (F_TYPE_EQUAL(magic, BTRFS_SUPER_MAGIC))
+ return "btrfs";
+
+ return NULL;
}
int home_augment_status(
uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX, disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
_cleanup_(json_variant_unrefp) JsonVariant *j = NULL, *v = NULL, *m = NULL, *status = NULL;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ statfs_f_type_t magic;
+ const char *fstype;
+ mode_t access_mode;
HomeState state;
sd_id128_t id;
int r;
&disk_usage,
&disk_free,
&disk_ceiling,
- &disk_floor);
+ &disk_floor,
+ &magic,
+ &access_mode);
if (r < 0)
return r;
+ fstype = fstype_magic_to_name(magic);
+
if (disk_floor == UINT64_MAX || (disk_usage != UINT64_MAX && disk_floor < disk_usage))
disk_floor = disk_usage;
if (disk_floor == UINT64_MAX || disk_floor < USER_DISK_SIZE_MIN)
JSON_BUILD_PAIR_CONDITION(disk_free != UINT64_MAX, "diskFree", JSON_BUILD_UNSIGNED(disk_free)),
JSON_BUILD_PAIR_CONDITION(disk_ceiling != UINT64_MAX, "diskCeiling", JSON_BUILD_UNSIGNED(disk_ceiling)),
JSON_BUILD_PAIR_CONDITION(disk_floor != UINT64_MAX, "diskFloor", JSON_BUILD_UNSIGNED(disk_floor)),
- JSON_BUILD_PAIR_CONDITION(h->signed_locally >= 0, "signedLocally", JSON_BUILD_BOOLEAN(h->signed_locally))
+ JSON_BUILD_PAIR_CONDITION(h->signed_locally >= 0, "signedLocally", JSON_BUILD_BOOLEAN(h->signed_locally)),
+ JSON_BUILD_PAIR_CONDITION(fstype, "fileSystemType", JSON_BUILD_STRING(fstype)),
+ JSON_BUILD_PAIR_CONDITION(access_mode != MODE_INVALID, "accessMode", JSON_BUILD_UNSIGNED(access_mode))
));
if (r < 0)
return r;
static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
static const JsonDispatch status_dispatch_table[] = {
- { "diskUsage", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_usage), 0 },
- { "diskFree", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_free), 0 },
- { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
- { "diskCeiling", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_ceiling), 0 },
- { "diskFloor", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_floor), 0 },
- { "state", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, state), JSON_SAFE },
- { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
- { "signedLocally", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, signed_locally), 0 },
- { "goodAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, good_authentication_counter), 0 },
- { "badAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, bad_authentication_counter), 0 },
- { "lastGoodAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_good_authentication_usec), 0 },
- { "lastBadAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_bad_authentication_usec), 0 },
- { "rateLimitBeginUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
- { "rateLimitCount", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
- { "removable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, removable), 0 },
+ { "diskUsage", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_usage), 0 },
+ { "diskFree", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_free), 0 },
+ { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
+ { "diskCeiling", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_ceiling), 0 },
+ { "diskFloor", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_floor), 0 },
+ { "state", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, state), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
+ { "signedLocally", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, signed_locally), 0 },
+ { "goodAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, good_authentication_counter), 0 },
+ { "badAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, bad_authentication_counter), 0 },
+ { "lastGoodAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_good_authentication_usec), 0 },
+ { "lastBadAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_bad_authentication_usec), 0 },
+ { "rateLimitBeginUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
+ { "rateLimitCount", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
+ { "removable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, removable), 0 },
+ { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
+ { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
{},
};