blob: 337daaa16a75f8ac3850a25b9e2c6e48d24b6126 [file] [log] [blame]
Lorenz Brun1d801752020-04-02 09:24:51 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
Serge Bazanski216fe7b2021-05-21 18:36:16 +020017// Package quotactl implements a low-level wrapper around the modern portion of
18// Linux's quotactl() syscall. See the fsquota package for a nicer interface to
19// the most common part of this API.
Lorenz Brun1d801752020-04-02 09:24:51 +020020package quotactl
21
22import (
23 "fmt"
24 "unsafe"
25
26 "golang.org/x/sys/unix"
27)
28
29type QuotaType uint
30
31const (
32 QuotaTypeUser QuotaType = iota
33 QuotaTypeGroup
34 QuotaTypeProject
35)
36
37const (
38 Q_SYNC uint = ((0x800001 + iota) << 8)
39 Q_QUOTAON
40 Q_QUOTAOFF
41 Q_GETFMT
42 Q_GETINFO
43 Q_SETINFO
44 Q_GETQUOTA
45 Q_SETQUOTA
46 Q_GETNEXTQUOTA
47)
48
49const (
50 FlagBLimitsValid = 1 << iota
51 FlagSpaceValid
52 FlagILimitsValid
53 FlagInodesValid
54 FlagBTimeValid
55 FlagITimeValid
56)
57
58type DQInfo struct {
59 Bgrace uint64
60 Igrace uint64
61 Flags uint32
62 Valid uint32
63}
64
65type Quota struct {
66 BHardLimit uint64 // Both Byte limits are prescaled by 1024 (so are in KiB), but CurSpace is in B
67 BSoftLimit uint64
68 CurSpace uint64
69 IHardLimit uint64
70 ISoftLimit uint64
71 CurInodes uint64
72 BTime uint64
73 ITime uint64
74 Valid uint32
75}
76
77type NextDQBlk struct {
78 HardLimitBytes uint64
79 SoftLimitBytes uint64
80 CurrentBytes uint64
81 HardLimitInodes uint64
82 SoftLimitInodes uint64
83 CurrentInodes uint64
84 BTime uint64
85 ITime uint64
86 Valid uint32
87 ID uint32
88}
89
90type QuotaFormat uint32
91
92// Collected from quota_format_type structs
93const (
94 // QuotaFormatNone is a special case where all quota information is
95 // stored inside filesystem metadata and thus requires no quotaFilePath.
96 QuotaFormatNone QuotaFormat = 0
97 QuotaFormatVFSOld QuotaFormat = 1
98 QuotaFormatVFSV0 QuotaFormat = 2
99 QuotaFormatOCFS2 QuotaFormat = 3
100 QuotaFormatVFSV1 QuotaFormat = 4
101)
102
103// QuotaOn turns quota accounting and enforcement on
104func QuotaOn(device string, qtype QuotaType, quotaFormat QuotaFormat, quotaFilePath string) error {
105 devArg, err := unix.BytePtrFromString(device)
106 if err != nil {
107 return err
108 }
109 pathArg, err := unix.BytePtrFromString(quotaFilePath)
110 if err != nil {
111 return err
112 }
113 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_QUOTAON|uint(qtype)), uintptr(unsafe.Pointer(devArg)), uintptr(quotaFormat), uintptr(unsafe.Pointer(pathArg)), 0, 0)
114 if err != unix.Errno(0) {
115 return err
116 }
117 return nil
118}
119
120// QuotaOff turns quotas off
121func QuotaOff(device string, qtype QuotaType) error {
122 devArg, err := unix.BytePtrFromString(device)
123 if err != nil {
124 return err
125 }
126 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_QUOTAOFF|uint(qtype)), uintptr(unsafe.Pointer(devArg)), 0, 0, 0, 0)
127 if err != unix.Errno(0) {
128 return err
129 }
130 return nil
131}
132
133// GetFmt gets the quota format used on given filesystem
Lorenz Brun5999e922021-01-27 18:53:54 +0100134func GetFmt(device string, qtype QuotaType) (QuotaFormat, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200135 var fmt uint32
136 devArg, err := unix.BytePtrFromString(device)
137 if err != nil {
138 return 0, err
139 }
140 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_GETFMT|uint(qtype)), uintptr(unsafe.Pointer(devArg)), 0, uintptr(unsafe.Pointer(&fmt)), 0, 0)
141 if err != unix.Errno(0) {
142 return 0, err
143 }
Lorenz Brun5999e922021-01-27 18:53:54 +0100144 return QuotaFormat(fmt), nil
Lorenz Brun1d801752020-04-02 09:24:51 +0200145}
146
147// GetInfo gets information about quota files
148func GetInfo(device string, qtype QuotaType) (*DQInfo, error) {
149 var info DQInfo
150 devArg, err := unix.BytePtrFromString(device)
151 if err != nil {
152 return nil, err
153 }
154 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_GETINFO|uint(qtype)), uintptr(unsafe.Pointer(devArg)), 0, uintptr(unsafe.Pointer(&info)), 0, 0)
155 if err != unix.Errno(0) {
156 return nil, err
157 }
158 return &info, nil
159}
160
161// SetInfo sets information about quota files
162func SetInfo(device string, qtype QuotaType, info *DQInfo) error {
163 devArg, err := unix.BytePtrFromString(device)
164 if err != nil {
165 return err
166 }
167 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SETINFO|uint(qtype)), uintptr(unsafe.Pointer(devArg)), 0, uintptr(unsafe.Pointer(info)), 0, 0)
168 if err != unix.Errno(0) {
169 return err
170 }
171 return nil
172}
173
174// GetQuota gets user quota structure
175func GetQuota(device string, qtype QuotaType, id uint32) (*Quota, error) {
176 var info Quota
177 devArg, err := unix.BytePtrFromString(device)
178 if err != nil {
179 return nil, err
180 }
181 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_GETQUOTA|uint(qtype)), uintptr(unsafe.Pointer(devArg)), uintptr(id), uintptr(unsafe.Pointer(&info)), 0, 0)
182 if err != unix.Errno(0) {
183 return nil, err
184 }
185 return &info, nil
186}
187
188// GetNextQuota gets disk limits and usage > ID
189func GetNextQuota(device string, qtype QuotaType, id uint32) (*NextDQBlk, error) {
190 var info NextDQBlk
191 devArg, err := unix.BytePtrFromString(device)
192 if err != nil {
193 return nil, err
194 }
195 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_GETNEXTQUOTA|uint(qtype)), uintptr(unsafe.Pointer(devArg)), uintptr(id), uintptr(unsafe.Pointer(&info)), 0, 0)
196 if err != unix.Errno(0) {
197 return nil, err
198 }
199 return &info, nil
200}
201
202// SetQuota sets the given quota
203func SetQuota(device string, qtype QuotaType, id uint32, quota *Quota) error {
204 devArg, err := unix.BytePtrFromString(device)
205 if err != nil {
206 return err
207 }
208 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SETQUOTA|uint(qtype)), uintptr(unsafe.Pointer(devArg)), uintptr(id), uintptr(unsafe.Pointer(quota)), 0, 0)
209 if err != unix.Errno(0) {
210 return fmt.Errorf("failed to set quota: %w", err)
211 }
212 return nil
213}
214
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200215// Sync syncs disk copy of filesystems quotas. If device is empty it syncs all
216// filesystems.
Lorenz Brun1d801752020-04-02 09:24:51 +0200217func Sync(device string) error {
218 if device != "" {
219 devArg, err := unix.BytePtrFromString(device)
220 if err != nil {
221 return err
222 }
223 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SYNC), uintptr(unsafe.Pointer(devArg)), 0, 0, 0, 0)
224 if err != unix.Errno(0) {
225 return err
226 }
227 } else {
228 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SYNC), 0, 0, 0, 0, 0)
229 if err != unix.Errno(0) {
230 return err
231 }
232 }
233 return nil
234}