blob: a246b7ce81fa637778ea920217b974ddfa06cd75 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brun1d801752020-04-02 09:24:51 +02002// SPDX-License-Identifier: Apache-2.0
Lorenz Brun1d801752020-04-02 09:24:51 +02003
Serge Bazanski216fe7b2021-05-21 18:36:16 +02004// Package quotactl implements a low-level wrapper around the modern portion of
5// Linux's quotactl() syscall. See the fsquota package for a nicer interface to
6// the most common part of this API.
Lorenz Brun1d801752020-04-02 09:24:51 +02007package quotactl
8
9import (
10 "fmt"
Lorenz Brun531e2c22021-11-17 20:00:05 +010011 "os"
Lorenz Brun1d801752020-04-02 09:24:51 +020012 "unsafe"
13
14 "golang.org/x/sys/unix"
15)
16
17type QuotaType uint
18
19const (
20 QuotaTypeUser QuotaType = iota
21 QuotaTypeGroup
22 QuotaTypeProject
23)
24
25const (
Tim Windelschmidt1d3a83d2024-04-11 01:40:58 +020026 Q_SYNC uint = (0x800001 + iota) << 8
Lorenz Brun1d801752020-04-02 09:24:51 +020027 Q_QUOTAON
28 Q_QUOTAOFF
29 Q_GETFMT
30 Q_GETINFO
31 Q_SETINFO
32 Q_GETQUOTA
33 Q_SETQUOTA
34 Q_GETNEXTQUOTA
35)
36
37const (
38 FlagBLimitsValid = 1 << iota
39 FlagSpaceValid
40 FlagILimitsValid
41 FlagInodesValid
42 FlagBTimeValid
43 FlagITimeValid
44)
45
46type DQInfo struct {
47 Bgrace uint64
48 Igrace uint64
49 Flags uint32
50 Valid uint32
51}
52
53type Quota struct {
54 BHardLimit uint64 // Both Byte limits are prescaled by 1024 (so are in KiB), but CurSpace is in B
55 BSoftLimit uint64
56 CurSpace uint64
57 IHardLimit uint64
58 ISoftLimit uint64
59 CurInodes uint64
60 BTime uint64
61 ITime uint64
62 Valid uint32
63}
64
65type NextDQBlk struct {
66 HardLimitBytes uint64
67 SoftLimitBytes uint64
68 CurrentBytes uint64
69 HardLimitInodes uint64
70 SoftLimitInodes uint64
71 CurrentInodes uint64
72 BTime uint64
73 ITime uint64
74 Valid uint32
75 ID uint32
76}
77
78type QuotaFormat uint32
79
80// Collected from quota_format_type structs
81const (
82 // QuotaFormatNone is a special case where all quota information is
83 // stored inside filesystem metadata and thus requires no quotaFilePath.
84 QuotaFormatNone QuotaFormat = 0
85 QuotaFormatVFSOld QuotaFormat = 1
86 QuotaFormatVFSV0 QuotaFormat = 2
87 QuotaFormatOCFS2 QuotaFormat = 3
88 QuotaFormatVFSV1 QuotaFormat = 4
89)
90
91// QuotaOn turns quota accounting and enforcement on
Lorenz Brun531e2c22021-11-17 20:00:05 +010092func QuotaOn(fd *os.File, qtype QuotaType, quotaFormat QuotaFormat, quotaFilePath string) error {
Lorenz Brun1d801752020-04-02 09:24:51 +020093 pathArg, err := unix.BytePtrFromString(quotaFilePath)
94 if err != nil {
95 return err
96 }
Tim Windelschmidt06c19642024-04-23 15:07:40 +020097 _, _, errNo := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_QUOTAON|uint(qtype)), uintptr(quotaFormat), uintptr(unsafe.Pointer(pathArg)), 0, 0)
98 if errNo != unix.Errno(0) {
99 return errNo
Lorenz Brun1d801752020-04-02 09:24:51 +0200100 }
101 return nil
102}
103
104// QuotaOff turns quotas off
Lorenz Brun531e2c22021-11-17 20:00:05 +0100105func QuotaOff(fd *os.File, qtype QuotaType) error {
106 _, _, 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 +0200107 if err != unix.Errno(0) {
108 return err
109 }
110 return nil
111}
112
113// GetFmt gets the quota format used on given filesystem
Lorenz Brun531e2c22021-11-17 20:00:05 +0100114func GetFmt(fd *os.File, qtype QuotaType) (QuotaFormat, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200115 var fmt uint32
Lorenz Brun531e2c22021-11-17 20:00:05 +0100116 _, _, 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 +0200117 if err != unix.Errno(0) {
118 return 0, err
119 }
Lorenz Brun5999e922021-01-27 18:53:54 +0100120 return QuotaFormat(fmt), nil
Lorenz Brun1d801752020-04-02 09:24:51 +0200121}
122
123// GetInfo gets information about quota files
Lorenz Brun531e2c22021-11-17 20:00:05 +0100124func GetInfo(fd *os.File, qtype QuotaType) (*DQInfo, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200125 var info DQInfo
Lorenz Brun531e2c22021-11-17 20:00:05 +0100126 _, _, 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 +0200127 if err != unix.Errno(0) {
128 return nil, err
129 }
130 return &info, nil
131}
132
133// SetInfo sets information about quota files
Lorenz Brun531e2c22021-11-17 20:00:05 +0100134func SetInfo(fd *os.File, qtype QuotaType, info *DQInfo) error {
135 _, _, 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 +0200136 if err != unix.Errno(0) {
137 return err
138 }
139 return nil
140}
141
142// GetQuota gets user quota structure
Lorenz Brun531e2c22021-11-17 20:00:05 +0100143func GetQuota(fd *os.File, qtype QuotaType, id uint32) (*Quota, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200144 var info Quota
Lorenz Brun531e2c22021-11-17 20:00:05 +0100145 _, _, 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 +0200146 if err != unix.Errno(0) {
147 return nil, err
148 }
149 return &info, nil
150}
151
152// GetNextQuota gets disk limits and usage > ID
Lorenz Brun531e2c22021-11-17 20:00:05 +0100153func GetNextQuota(fd *os.File, qtype QuotaType, id uint32) (*NextDQBlk, error) {
Lorenz Brun1d801752020-04-02 09:24:51 +0200154 var info NextDQBlk
Lorenz Brun531e2c22021-11-17 20:00:05 +0100155 _, _, 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 +0200156 if err != unix.Errno(0) {
157 return nil, err
158 }
159 return &info, nil
160}
161
162// SetQuota sets the given quota
Lorenz Brun531e2c22021-11-17 20:00:05 +0100163func SetQuota(fd *os.File, qtype QuotaType, id uint32, quota *Quota) error {
164 _, _, 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 +0200165 if err != unix.Errno(0) {
166 return fmt.Errorf("failed to set quota: %w", err)
167 }
168 return nil
169}
170
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200171// Sync syncs disk copy of filesystems quotas. If device is empty it syncs all
172// filesystems.
Lorenz Brun531e2c22021-11-17 20:00:05 +0100173func Sync(fd *os.File) error {
174 if fd != nil {
175 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL_FD, fd.Fd(), uintptr(Q_SYNC), 0, 0, 0, 0)
Lorenz Brun1d801752020-04-02 09:24:51 +0200176 if err != unix.Errno(0) {
177 return err
178 }
179 } else {
180 _, _, err := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(Q_SYNC), 0, 0, 0, 0, 0)
181 if err != unix.Errno(0) {
182 return err
183 }
184 }
185 return nil
186}