blob: 08be6e0762db38ec8f90a7d0081aa2ecf20da137 [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"
Lorenz Brun531e2c22021-11-17 20:00:05 +010024 "os"
Lorenz Brun1d801752020-04-02 09:24:51 +020025 "unsafe"
26
27 "golang.org/x/sys/unix"
28)
29
30type QuotaType uint
31
32const (
33 QuotaTypeUser QuotaType = iota
34 QuotaTypeGroup
35 QuotaTypeProject
36)
37
38const (
39 Q_SYNC uint = ((0x800001 + iota) << 8)
40 Q_QUOTAON
41 Q_QUOTAOFF
42 Q_GETFMT
43 Q_GETINFO
44 Q_SETINFO
45 Q_GETQUOTA
46 Q_SETQUOTA
47 Q_GETNEXTQUOTA
48)
49
50const (
51 FlagBLimitsValid = 1 << iota
52 FlagSpaceValid
53 FlagILimitsValid
54 FlagInodesValid
55 FlagBTimeValid
56 FlagITimeValid
57)
58
59type DQInfo struct {
60 Bgrace uint64
61 Igrace uint64
62 Flags uint32
63 Valid uint32
64}
65
66type Quota struct {
67 BHardLimit uint64 // Both Byte limits are prescaled by 1024 (so are in KiB), but CurSpace is in B
68 BSoftLimit uint64
69 CurSpace uint64
70 IHardLimit uint64
71 ISoftLimit uint64
72 CurInodes uint64
73 BTime uint64
74 ITime uint64
75 Valid uint32
76}
77
78type NextDQBlk struct {
79 HardLimitBytes uint64
80 SoftLimitBytes uint64
81 CurrentBytes uint64
82 HardLimitInodes uint64
83 SoftLimitInodes uint64
84 CurrentInodes uint64
85 BTime uint64
86 ITime uint64
87 Valid uint32
88 ID uint32
89}
90
91type QuotaFormat uint32
92
93// Collected from quota_format_type structs
94const (
95 // QuotaFormatNone is a special case where all quota information is
96 // stored inside filesystem metadata and thus requires no quotaFilePath.
97 QuotaFormatNone QuotaFormat = 0
98 QuotaFormatVFSOld QuotaFormat = 1
99 QuotaFormatVFSV0 QuotaFormat = 2
100 QuotaFormatOCFS2 QuotaFormat = 3
101 QuotaFormatVFSV1 QuotaFormat = 4
102)
103
104// QuotaOn turns quota accounting and enforcement on
Lorenz Brun531e2c22021-11-17 20:00:05 +0100105func QuotaOn(fd *os.File, qtype QuotaType, quotaFormat QuotaFormat, quotaFilePath string) error {
Lorenz Brun1d801752020-04-02 09:24:51 +0200106 pathArg, err := unix.BytePtrFromString(quotaFilePath)
107 if err != nil {
108 return err
109 }
Lorenz Brun531e2c22021-11-17 20:00:05 +0100110 _, _, err = unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_QUOTAON|uint(qtype)), uintptr(quotaFormat), uintptr(unsafe.Pointer(pathArg)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200111 if err != unix.Errno(0) {
112 return err
113 }
114 return nil
115}
116
117// QuotaOff turns quotas off
Lorenz Brun531e2c22021-11-17 20:00:05 +0100118func QuotaOff(fd *os.File, qtype QuotaType) error {
119 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_QUOTAOFF|uint(qtype)), 0, 0, 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200120 if err != unix.Errno(0) {
121 return err
122 }
123 return nil
124}
125
126// GetFmt gets the quota format used on given filesystem
Lorenz Brun531e2c22021-11-17 20:00:05 +0100127func GetFmt(fd *os.File, qtype QuotaType) (QuotaFormat, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200128 var fmt uint32
Lorenz Brun531e2c22021-11-17 20:00:05 +0100129 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_GETFMT|uint(qtype)), 0, uintptr(unsafe.Pointer(&fmt)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200130 if err != unix.Errno(0) {
131 return 0, err
132 }
Lorenz Brun5999e922021-01-27 18:53:54 +0100133 return QuotaFormat(fmt), nil
Lorenz Brun1d801752020-04-02 09:24:51 +0200134}
135
136// GetInfo gets information about quota files
Lorenz Brun531e2c22021-11-17 20:00:05 +0100137func GetInfo(fd *os.File, qtype QuotaType) (*DQInfo, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200138 var info DQInfo
Lorenz Brun531e2c22021-11-17 20:00:05 +0100139 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_GETINFO|uint(qtype)), 0, uintptr(unsafe.Pointer(&info)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200140 if err != unix.Errno(0) {
141 return nil, err
142 }
143 return &info, nil
144}
145
146// SetInfo sets information about quota files
Lorenz Brun531e2c22021-11-17 20:00:05 +0100147func SetInfo(fd *os.File, qtype QuotaType, info *DQInfo) error {
148 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_SETINFO|uint(qtype)), 0, uintptr(unsafe.Pointer(info)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200149 if err != unix.Errno(0) {
150 return err
151 }
152 return nil
153}
154
155// GetQuota gets user quota structure
Lorenz Brun531e2c22021-11-17 20:00:05 +0100156func GetQuota(fd *os.File, qtype QuotaType, id uint32) (*Quota, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200157 var info Quota
Lorenz Brun531e2c22021-11-17 20:00:05 +0100158 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_GETQUOTA|uint(qtype)), uintptr(id), uintptr(unsafe.Pointer(&info)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200159 if err != unix.Errno(0) {
160 return nil, err
161 }
162 return &info, nil
163}
164
165// GetNextQuota gets disk limits and usage > ID
Lorenz Brun531e2c22021-11-17 20:00:05 +0100166func GetNextQuota(fd *os.File, qtype QuotaType, id uint32) (*NextDQBlk, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200167 var info NextDQBlk
Lorenz Brun531e2c22021-11-17 20:00:05 +0100168 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_GETNEXTQUOTA|uint(qtype)), uintptr(id), uintptr(unsafe.Pointer(&info)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200169 if err != unix.Errno(0) {
170 return nil, err
171 }
172 return &info, nil
173}
174
175// SetQuota sets the given quota
Lorenz Brun531e2c22021-11-17 20:00:05 +0100176func SetQuota(fd *os.File, qtype QuotaType, id uint32, quota *Quota) error {
177 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_SETQUOTA|uint(qtype)), uintptr(id), uintptr(unsafe.Pointer(quota)), 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200178 if err != unix.Errno(0) {
179 return fmt.Errorf("failed to set quota: %w", err)
180 }
181 return nil
182}
183
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200184// Sync syncs disk copy of filesystems quotas. If device is empty it syncs all
185// filesystems.
Lorenz Brun531e2c22021-11-17 20:00:05 +0100186func Sync(fd *os.File) error {
187 if fd != nil {
188 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_SYNC), 0, 0, 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200189 if err != unix.Errno(0) {
190 return err
191 }
192 } else {
193 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SYNC), 0, 0, 0, 0, 0)
194 if err != unix.Errno(0) {
195 return err
196 }
197 }
198 return nil
199}