1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #include <sys/quota.h>
3 
4 #include "blockdev-util.h"
5 #include "btrfs-util.h"
6 #include "errno-util.h"
7 #include "format-util.h"
8 #include "homework-quota.h"
9 #include "missing_magic.h"
10 #include "quota-util.h"
11 #include "stat-util.h"
12 #include "user-util.h"
13 
home_update_quota_btrfs(UserRecord * h,const char * path)14 int home_update_quota_btrfs(UserRecord *h, const char *path) {
15         int r;
16 
17         assert(h);
18         assert(path);
19 
20         if (h->disk_size == UINT64_MAX)
21                 return 0;
22 
23         /* If the user wants quota, enable it */
24         r = btrfs_quota_enable(path, true);
25         if (r == -ENOTTY)
26                 return log_error_errno(r, "No btrfs quota support on subvolume %s.", path);
27         if (r < 0)
28                 return log_error_errno(r, "Failed to enable btrfs quota support on %s.", path);
29 
30         r = btrfs_qgroup_set_limit(path, 0, h->disk_size);
31         if (r < 0)
32                 return log_error_errno(r, "Faled to set disk quota on subvolume %s: %m", path);
33 
34         log_info("Set btrfs quota.");
35 
36         return 0;
37 }
38 
home_update_quota_classic(UserRecord * h,const char * path)39 int home_update_quota_classic(UserRecord *h, const char *path) {
40         struct dqblk req;
41         dev_t devno;
42         int r;
43 
44         assert(h);
45         assert(uid_is_valid(h->uid));
46         assert(path);
47 
48         if (h->disk_size == UINT64_MAX)
49                 return 0;
50 
51         r = get_block_device(path, &devno);
52         if (r < 0)
53                 return log_error_errno(r, "Failed to determine block device of %s: %m", path);
54         if (devno == 0)
55                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system %s not backed by a block device.", path);
56 
57         r = quotactl_devnum(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
58         if (r < 0) {
59                 if (ERRNO_IS_NOT_SUPPORTED(r))
60                         return log_error_errno(r, "No UID quota support on %s.", path);
61 
62                 if (r != -ESRCH)
63                         return log_error_errno(r, "Failed to query disk quota for UID " UID_FMT ": %m", h->uid);
64 
65                 zero(req);
66         } else {
67                 /* Shortcut things if everything is set up properly already */
68                 if (FLAGS_SET(req.dqb_valid, QIF_BLIMITS) && h->disk_size / QIF_DQBLKSIZE == req.dqb_bhardlimit) {
69                         log_info("Configured quota already matches the intended setting, not updating quota.");
70                         return 0;
71                 }
72         }
73 
74         req.dqb_valid = QIF_BLIMITS;
75         req.dqb_bsoftlimit = req.dqb_bhardlimit = h->disk_size / QIF_DQBLKSIZE;
76 
77         r = quotactl_devnum(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
78         if (r < 0) {
79                 if (r == -ESRCH)
80                         return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "UID quota not available on %s.", path);
81 
82                 return log_error_errno(r, "Failed to set disk quota for UID " UID_FMT ": %m", h->uid);
83         }
84 
85         log_info("Updated per-UID quota.");
86 
87         return 0;
88 }
89 
home_update_quota_auto(UserRecord * h,const char * path)90 int home_update_quota_auto(UserRecord *h, const char *path) {
91         struct statfs sfs;
92         int r;
93 
94         assert(h);
95 
96         if (h->disk_size == UINT64_MAX)
97                 return 0;
98 
99         if (!path) {
100                 path = user_record_image_path(h);
101                 if (!path)
102                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home record lacks image path.");
103         }
104 
105         if (statfs(path, &sfs) < 0)
106                 return log_error_errno(errno, "Failed to statfs() file system: %m");
107 
108         if (is_fs_type(&sfs, XFS_SB_MAGIC) ||
109             is_fs_type(&sfs, EXT4_SUPER_MAGIC))
110                 return home_update_quota_classic(h, path);
111 
112         if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
113 
114                 r = btrfs_is_subvol(path);
115                 if (r < 0)
116                         return log_error_errno(errno, "Failed to test if %s is a subvolume: %m", path);
117                 if (r == 0)
118                         return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Directory %s is not a subvolume, cannot apply quota.", path);
119 
120                 return home_update_quota_btrfs(h, path);
121         }
122 
123         return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Type of directory %s not known, cannot apply quota.", path);
124 }
125