summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2010-05-07 00:41:12 +0000
committermckusick <mckusick@FreeBSD.org>2010-05-07 00:41:12 +0000
commite95ff34dacc330ba2cbfa7f426614e3dda40deff (patch)
tree40443c388266d280302f5035694a9fca8ff3cfcd /lib
parent37abb66e2f1dcd063d48c212b7423a4b32c6db80 (diff)
parentb25e55dcc52d6203a9ae995ca470a66b6483f71d (diff)
downloadFreeBSD-src-e95ff34dacc330ba2cbfa7f426614e3dda40deff.zip
FreeBSD-src-e95ff34dacc330ba2cbfa7f426614e3dda40deff.tar.gz
Merger of the quota64 project into head.
This joint work of Dag-Erling Smørgrav and myself updates the FFS quota system to support both traditional 32-bit and new 64-bit quotas (for those of you who want to put 2+Tb quotas on your users). By default quotas are not compiled into the kernel. To include them in your kernel configuration you need to specify: options QUOTA # Enable FFS quotas If you are already running with the current 32-bit quotas, they should continue to work just as they have in the past. If you wish to convert to using 64-bit quotas, use `quotacheck -c 64'; if you wish to revert from 64-bit quotas back to 32-bit quotas, use `quotacheck -c 32'. There is a new library of functions to simplify the use of the quota system, do `man quotafile' for details. If your application is currently using the quotactl(2), it is highly recommended that you convert your application to use the quotafile interface. Note that existing binaries will continue to work. Special thanks to John Kozubik of rsync.net for getting me interested in pursuing 64-bit quota support and for funding part of my development time on this project.
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/sys/quotactl.222
-rw-r--r--lib/libutil/Makefile12
-rw-r--r--lib/libutil/libutil.h17
-rw-r--r--lib/libutil/quotafile.3290
-rw-r--r--lib/libutil/quotafile.c593
5 files changed, 928 insertions, 6 deletions
diff --git a/lib/libc/sys/quotactl.2 b/lib/libc/sys/quotactl.2
index 4b4dca5..ff3cb4b 100644
--- a/lib/libc/sys/quotactl.2
+++ b/lib/libc/sys/quotactl.2
@@ -84,7 +84,7 @@ and group identifiers (GRPQUOTA).
The
.Dq ufs
specific commands are:
-.Bl -tag -width Q_QUOTAOFFxx
+.Bl -tag -width Q_GETQUOTASIZEx
.It Dv Q_QUOTAON
Enable disk quotas for the file system specified by
.Fa path .
@@ -110,6 +110,17 @@ and
.Fa id
arguments are unused.
Only the super-user may turn quotas off.
+.It Dv Q_GETQUOTASIZE
+Get the wordsize used to represent the quotas for the user or group
+(as determined by the command type).
+Possible values are 32 for the old-style quota file
+and 64 for the new-style quota file.
+The
+.Fa addr
+argument is a pointer to an integer into which the size is stored.
+The identifier
+.Fa id
+is not used.
.It Dv Q_GETQUOTA
Get disk quota limits and current usage for the user or group
(as determined by the command type) with identifier
@@ -177,9 +188,11 @@ The
argument
or the command type is invalid.
In
-.Dv Q_GETQUOTA
-and
+.Dv Q_GETQUOTASIZE ,
+.Dv Q_GETQUOTA ,
.Dv Q_SETQUOTA ,
+and
+.Dv Q_SETUSE ,
quotas are not currently enabled for this file system.
.Pp
The
@@ -208,7 +221,8 @@ Too many symbolic links were encountered in translating a pathname.
.It Bq Er EROFS
In
.Dv Q_QUOTAON ,
-the quota file resides on a read-only file system.
+either the file system on which quotas are to be enabled is mounted read-only
+or the quota file resides on a read-only file system.
.It Bq Er EIO
An
.Tn I/O
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
index d82d934..9dc35c0 100644
--- a/lib/libutil/Makefile
+++ b/lib/libutil/Makefile
@@ -12,7 +12,7 @@ SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \
hexdump.c humanize_number.c kinfo_getfile.c kinfo_getvmmap.c kld.c \
login_auth.c login_cap.c \
login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \
- pidfile.c property.c pty.c pw_util.c realhostname.c \
+ pidfile.c property.c pty.c pw_util.c quotafile.c realhostname.c \
stub.c trimdomain.c uucplock.c
INCS= libutil.h login_cap.h
@@ -29,7 +29,7 @@ MAN+= kld.3 login_auth.3 login_tty.3 pty.3 \
_secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \
realhostname_sa.3 trimdomain.3 fparseln.3 humanize_number.3 \
pidfile.3 flopen.3 expand_number.3 hexdump.3 \
- kinfo_getfile.3 kinfo_getvmmap.3
+ kinfo_getfile.3 kinfo_getvmmap.3 quotafile.3
MAN+= login.conf.5 auth.conf.5
MLINKS+= kld.3 kld_isloaded.3 kld.3 kld_load.3
MLINKS+= property.3 properties_read.3 property.3 properties_free.3
@@ -57,5 +57,13 @@ MLINKS+=pidfile.3 pidfile_open.3 \
pidfile.3 pidfile_write.3 \
pidfile.3 pidfile_close.3 \
pidfile.3 pidfile_remove.3
+MLINKS+=quotafile.3 quota_open.3 \
+ quotafile.3 quota_fsname.3 \
+ quotafile.3 quota_qfname.3 \
+ quotafile.3 quota_statfs.3 \
+ quotafile.3 quota_read.3 \
+ quotafile.3 quota_write_limits.3 \
+ quotafile.3 quota_write_usage.3 \
+ quotafile.3 quota_close.3
.include <bsd.lib.mk>
diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h
index 4c2ee3b..5b7ffad 100644
--- a/lib/libutil/libutil.h
+++ b/lib/libutil/libutil.h
@@ -164,6 +164,23 @@ int pidfile_close(struct pidfh *pfh);
int pidfile_remove(struct pidfh *pfh);
#endif
+#ifdef _UFS_UFS_QUOTA_H_
+struct quotafile;
+struct fstab;
+struct quotafile *quota_open(struct fstab *, int, int);
+void quota_close(struct quotafile *);
+int quota_on(struct quotafile *);
+int quota_off(struct quotafile *);
+const char *quota_fsname(const struct quotafile *);
+const char *quota_qfname(const struct quotafile *);
+int quota_maxid(struct quotafile *);
+int quota_check_path(const struct quotafile *, const char *path);
+int quota_read(struct quotafile *, struct dqblk *, int);
+int quota_write_limits(struct quotafile *, struct dqblk *, int);
+int quota_write_usage(struct quotafile *, struct dqblk *, int);
+int quota_convert(struct quotafile *, int);
+#endif
+
__END_DECLS
#define UU_LOCK_INUSE (1)
diff --git a/lib/libutil/quotafile.3 b/lib/libutil/quotafile.3
new file mode 100644
index 0000000..5702cec
--- /dev/null
+++ b/lib/libutil/quotafile.3
@@ -0,0 +1,290 @@
+.\"-
+.\" Copyright (c) 2009 Dag-Erling Coïdan Smørgrav and
+.\" Marshall Kirk McKusick. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 28, 2009
+.Dt QUOTAFILE 3
+.Os
+.Sh NAME
+.Nm quota_open
+.Nm quota_close
+.Nm quota_on
+.Nm quota_off
+.Nm quota_read
+.Nm quota_write_limits
+.Nm quota_write_usage
+.Nm quota_fsname
+.Nm quota_qfname
+.Nm quota_maxid
+.Nm quota_check_path
+.Nm quota_convert
+.Nd "Manipulate quotas"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/quota.h
+.In fcntl.h
+.In fstab.h
+.In libutil.h
+.Ft "struct quotafile *"
+.Fn quota_open "struct fstab *fs" "int quotatype" "int openflags"
+.Ft int
+.Fn quota_close "struct quotafile *qf"
+.Ft int
+.Fn quota_on "const struct quotafile *qf"
+.Ft int
+.Fn quota_off "const struct quotafile *qf"
+.Ft int
+.Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft int
+.Fn quota_write_limits "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft int
+.Fn quota_write_usage "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft "const char *"
+.Fn quota_fsname "const struct quotafile *qf"
+.Ft "const char *"
+.Fn quota_qfname "const struct quotafile *qf"
+.Ft int
+.Fn quota_maxid "const struct quotafile *qf"
+.Ft int
+.Fn quota_check_path "const struct quotafile *qf" "const char *path"
+.Ft int
+.Fn quota_convert "struct quotafile *qf" "int wordsize"
+.Sh DESCRIPTION
+These functions are designed to simplify access to filesystem quotas.
+If quotas are active on a filesystem,
+these functions will access them directly from the kernel using the
+.Fn quotactl
+system call.
+If quotas are not active,
+these functions will access them by reading and writing
+the quota files directly.
+.Pp
+The
+.Fn quota_open
+function takes a pointer to an
+.Vt fstab
+entry corresponding to the filesystem on which quotas
+are to be accessed.
+The
+.Va quotatype
+field indicates the type of quotas being sought, either
+.Dv USRQUOTA
+or
+.Dv GRPQUOTA .
+The
+.Va openflags
+are those used by the
+.Fn open
+system call, usually either
+.Dv O_RDONLY
+if the quotas are just to be read, or
+.Dv O_RDWR
+if the quotas are to be updated.
+The
+.Dv O_CREAT
+flag should be specified if a new quota file of the requested type
+should be created if it does not already exist.
+.Pp
+The
+.Fn quota_close
+function closes any open file descriptors and frees any storage
+associated with the filesystem and quota type referenced by
+.Va qf .
+.Pp
+The
+.Fn quota_on
+function enables quotas for the filesystem associated with its
+.Va qf
+argument which may have been opened
+.Dv O_RDONLY
+or
+.Dv O_RDWR .
+The
+.Fn quota_on
+function returns 0 if successful;
+otherwise the value\~-1 is returned and the global variable
+.Va errno
+is set to indicate the error, see
+.Xr quotactl 2
+for the possible errors.
+.Pp
+The
+.Fn quota_off
+function disables quotas for the filesystem associated with its
+.Va qf
+argument which may have been opened
+.Dv O_RDONLY
+or
+.Dv O_RDWR .
+The
+.Fn quota_off
+function returns 0 if successful;
+otherwise the value\~-1 is returned and the global variable
+.Va errno
+is set to indicate the error, see
+.Xr quotactl 2
+for the possible errors.
+.Pp
+The
+.Fn quota_read
+function reads the quota from the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+into the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_write_limits
+function updates the limit fields (but not the usage fields)
+for the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+from the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_write_usage
+function updates the usage fields (but not the limit fields)
+for the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+from the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_fsname
+function returns a pointer to a buffer containing the path to the root
+of the file system that corresponds to its
+.Va qf
+argument, as listed in
+.Pa /etc/fstab .
+Note that this may be a symbolic link to the actual directory.
+.Pp
+The
+.Fn quota_qfname
+function returns a pointer to a buffer containing the name of the
+quota file that corresponds to its
+.Va qf
+argument.
+Note that this may be a symbolic link to the actual file.
+.Pp
+The
+.Fn quota_maxid
+function returns the maximum user (or group)
+.Va id
+contained in the quota file associated with its
+.Va qf
+argument.
+.Pp
+The
+.Fn quota_check_path
+function checks if the specified path is within the filesystem that
+corresponds to its
+.Va qf
+argument.
+If the
+.Va path
+argument refers to a symbolic link,
+.Fn quota_check_path
+will follow it.
+.Pp
+The
+.Fn quota_convert
+function converts the quota file associated with its
+.Va qf
+argument to the data size specified by its
+.Va wordsize
+argument.
+The supported wordsize arguments are 32 for the old 32-bit
+quota file format and 64 for the new 64-bit quota file format.
+The
+.Fn quota_convert
+function may only be called to operate on quota files that
+are not currently active.
+.Sh IMPLEMENTATION NOTES
+If the underlying quota file is in or converted to the old 32-bit format,
+limit and usage values written to the quota file will be clipped to 32 bits.
+.Sh RETURN VALUES
+If the filesystem has quotas associated with it,
+.Fn quota_open
+returns a pointer to a
+.Vt quotafile
+structure used in subsequent quota access calls.
+If the filesystem has no quotas, or access permission is denied
+.Dv NULL
+is returned and
+.Va errno
+is set to indicate the error.
+.Pp
+The
+.Fn quota_check_path
+function returns\~1 for a positive result and\~0 for a negative
+result.
+If an error occurs, it returns\~-1 and sets
+.Va errno
+to indicate the error.
+.Pp
+The
+.Fn quota_read ,
+.Fn quota_write_limits ,
+.Fn quota_write_usage ,
+.Fn quota_convert ,
+and
+.Fn quota_close
+functions return zero on success.
+On error they return\~-1
+and set
+.Va errno
+to indicate the error.
+.Sh SEE ALSO
+.Xr quotactl 2 ,
+.Xr quota.user 5 ,
+.Xr quota.group 5
+.Sh HISTORY
+The
+.Nm quotafile
+functions first appeared in
+.Fx 8.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm quotafile
+functions and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org
+and
+.An Marshall Kirk McKusick Aq mckusick@mckusick.com .
diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c
new file mode 100644
index 0000000..0fda5f6
--- /dev/null
+++ b/lib/libutil/quotafile.c
@@ -0,0 +1,593 @@
+/*-
+ * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008 Marshall Kirk McKusick
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/quota.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <libutil.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct quotafile {
+ int fd; /* -1 means using quotactl for access */
+ int accmode; /* access mode */
+ int wordsize; /* 32-bit or 64-bit limits */
+ int quotatype; /* USRQUOTA or GRPQUOTA */
+ dev_t dev; /* device */
+ char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */
+ char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */
+};
+
+static const char *qfextension[] = INITQFNAMES;
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+static int
+hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
+{
+ char *opt;
+ char *cp;
+ struct statfs sfb;
+ char buf[BUFSIZ];
+ static char initname, usrname[100], grpname[100];
+
+ /*
+ * 1) we only need one of these
+ * 2) fstab may specify a different filename
+ */
+ if (!initname) {
+ (void)snprintf(usrname, sizeof(usrname), "%s%s",
+ qfextension[USRQUOTA], QUOTAFILENAME);
+ (void)snprintf(grpname, sizeof(grpname), "%s%s",
+ qfextension[GRPQUOTA], QUOTAFILENAME);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ /*
+ * Ensure that the filesystem is mounted.
+ */
+ if (statfs(fs->fs_file, &sfb) != 0 ||
+ strcmp(fs->fs_file, sfb.f_mntonname)) {
+ return (0);
+ }
+ if (cp) {
+ strncpy(qfnamep, cp, qfbufsize);
+ } else {
+ (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
+ QUOTAFILENAME, qfextension[type]);
+ }
+ return (1);
+}
+
+struct quotafile *
+quota_open(struct fstab *fs, int quotatype, int openflags)
+{
+ struct quotafile *qf;
+ struct dqhdr64 dqh;
+ struct group *grp;
+ struct stat st;
+ int qcmd, serrno;
+
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ return (NULL);
+ if ((qf = calloc(1, sizeof(*qf))) == NULL)
+ return (NULL);
+ qf->fd = -1;
+ qf->quotatype = quotatype;
+ strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
+ if (stat(qf->fsname, &st) != 0)
+ goto error;
+ qf->dev = st.st_dev;
+ serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
+ qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
+ if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
+ return (qf);
+ if (serrno == 0) {
+ errno = EOPNOTSUPP;
+ goto error;
+ }
+ qf->accmode = openflags & O_ACCMODE;
+ if ((qf->fd = open(qf->qfname, qf->accmode)) < 0 &&
+ (openflags & O_CREAT) != O_CREAT)
+ goto error;
+ /* File open worked, so process it */
+ if (qf->fd != -1) {
+ qf->wordsize = 32;
+ switch (read(qf->fd, &dqh, sizeof(dqh))) {
+ case -1:
+ goto error;
+ case sizeof(dqh):
+ if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
+ /* no magic, assume 32 bits */
+ qf->wordsize = 32;
+ return (qf);
+ }
+ if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
+ be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
+ be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
+ /* correct magic, wrong version / lengths */
+ errno = EINVAL;
+ goto error;
+ }
+ qf->wordsize = 64;
+ return (qf);
+ default:
+ qf->wordsize = 32;
+ return (qf);
+ }
+ /* not reached */
+ }
+ /* open failed, but O_CREAT was specified, so create a new file */
+ if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0)
+ goto error;
+ qf->wordsize = 64;
+ memset(&dqh, 0, sizeof(dqh));
+ memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
+ dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
+ dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
+ dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
+ if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
+ /* it was one we created ourselves */
+ unlink(qf->qfname);
+ goto error;
+ }
+ grp = getgrnam(QUOTAGROUP);
+ fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
+ fchmod(qf->fd, 0640);
+ return (qf);
+error:
+ serrno = errno;
+ /* did we have an open file? */
+ if (qf->fd != -1)
+ close(qf->fd);
+ free(qf);
+ errno = serrno;
+ return (NULL);
+}
+
+void
+quota_close(struct quotafile *qf)
+{
+
+ if (qf->fd != -1)
+ close(qf->fd);
+ free(qf);
+}
+
+int
+quota_on(struct quotafile *qf)
+{
+ int qcmd;
+
+ qcmd = QCMD(Q_QUOTAON, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
+}
+
+int
+quota_off(struct quotafile *qf)
+{
+
+ return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
+}
+
+const char *
+quota_fsname(const struct quotafile *qf)
+{
+
+ return (qf->fsname);
+}
+
+const char *
+quota_qfname(const struct quotafile *qf)
+{
+
+ return (qf->qfname);
+}
+
+int
+quota_check_path(const struct quotafile *qf, const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) == -1)
+ return (-1);
+ return (st.st_dev == qf->dev);
+}
+
+int
+quota_maxid(struct quotafile *qf)
+{
+ struct stat st;
+ int maxid;
+
+ if (stat(qf->qfname, &st) < 0)
+ return (0);
+ switch (qf->wordsize) {
+ case 32:
+ maxid = st.st_size / sizeof(struct dqblk32) - 1;
+ break;
+ case 64:
+ maxid = st.st_size / sizeof(struct dqblk64) - 2;
+ break;
+ default:
+ maxid = 0;
+ break;
+ }
+ return (maxid > 0 ? maxid : 0);
+}
+
+static int
+quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk32 dqb32;
+ off_t off;
+
+ off = id * sizeof(struct dqblk32);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
+ case 0:
+ memset(dqb, 0, sizeof(*dqb));
+ return (0);
+ case sizeof(dqb32):
+ dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
+ dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
+ dqb->dqb_curblocks = dqb32.dqb_curblocks;
+ dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
+ dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
+ dqb->dqb_curinodes = dqb32.dqb_curinodes;
+ dqb->dqb_btime = dqb32.dqb_btime;
+ dqb->dqb_itime = dqb32.dqb_itime;
+ return (0);
+ default:
+ return (-1);
+ }
+}
+
+static int
+quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk64 dqb64;
+ off_t off;
+
+ off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
+ case 0:
+ memset(dqb, 0, sizeof(*dqb));
+ return (0);
+ case sizeof(dqb64):
+ dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
+ dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
+ dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
+ dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
+ dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
+ dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
+ dqb->dqb_btime = be64toh(dqb64.dqb_btime);
+ dqb->dqb_itime = be64toh(dqb64.dqb_itime);
+ return (0);
+ default:
+ return (-1);
+ }
+}
+
+int
+quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_read32(qf, dqb, id));
+ case 64:
+ return (quota_read64(qf, dqb, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
+
+static int
+quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
+{
+ struct dqblk32 dqb32;
+ off_t off;
+
+ dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
+ dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
+ dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
+ dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
+ dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
+ dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
+ dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
+ dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
+
+ off = id * sizeof(struct dqblk32);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
+ return (0);
+ return (-1);
+}
+
+static int
+quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
+{
+ struct dqblk64 dqb64;
+ off_t off;
+
+ dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
+ dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
+ dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
+ dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
+ dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
+ dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
+ dqb64.dqb_btime = htobe64(dqb->dqb_btime);
+ dqb64.dqb_itime = htobe64(dqb->dqb_itime);
+
+ off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
+ return (0);
+ return (-1);
+}
+
+int
+quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk dqbuf;
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_SETUSE, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ /*
+ * Have to do read-modify-write of quota in file.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (quota_read(qf, &dqbuf, id) != 0)
+ return (-1);
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it.
+ */
+ if (dqbuf.dqb_bsoftlimit && id != 0 &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
+ dqbuf.dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit && id != 0 &&
+ dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
+ dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
+ dqbuf.dqb_itime = 0;
+ dqbuf.dqb_curinodes = dqb->dqb_curinodes;
+ dqbuf.dqb_curblocks = dqb->dqb_curblocks;
+ /*
+ * Write it back.
+ */
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_write32(qf, &dqbuf, id));
+ case 64:
+ return (quota_write64(qf, &dqbuf, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+int
+quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk dqbuf;
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ /*
+ * Have to do read-modify-write of quota in file.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (quota_read(qf, &dqbuf, id) != 0)
+ return (-1);
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it
+ * or if there previously was no soft limit, but
+ * now have one and are over it.
+ */
+ if (dqbuf.dqb_bsoftlimit && id != 0 &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
+ dqb->dqb_btime = 0;
+ if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
+ dqb->dqb_bsoftlimit > 0 &&
+ dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
+ dqb->dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit && id != 0 &&
+ dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
+ dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
+ dqb->dqb_itime = 0;
+ if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
+ dqb->dqb_isoftlimit > 0 &&
+ dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
+ dqb->dqb_itime = 0;
+ dqb->dqb_curinodes = dqbuf.dqb_curinodes;
+ dqb->dqb_curblocks = dqbuf.dqb_curblocks;
+ /*
+ * Write it back.
+ */
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_write32(qf, dqb, id));
+ case 64:
+ return (quota_write64(qf, dqb, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+/*
+ * Convert a quota file from one format to another.
+ */
+int
+quota_convert(struct quotafile *qf, int wordsize)
+{
+ struct quotafile *newqf;
+ struct dqhdr64 dqh;
+ struct dqblk dqblk;
+ struct group *grp;
+ int serrno, maxid, id, fd;
+
+ /*
+ * Quotas must not be active and quotafile must be open
+ * for reading and writing.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
+ errno = EBADF;
+ return (-1);
+ }
+ if ((wordsize != 32 && wordsize != 64) ||
+ wordsize == qf->wordsize) {
+ errno = EINVAL;
+ return (-1);
+ }
+ maxid = quota_maxid(qf);
+ if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ *newqf = *qf;
+ snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
+ qf->wordsize);
+ if (rename(qf->qfname, newqf->qfname) < 0) {
+ free(newqf);
+ return (-1);
+ }
+ if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
+ serrno = errno;
+ goto error;
+ }
+ newqf->wordsize = wordsize;
+ if (wordsize == 64) {
+ memset(&dqh, 0, sizeof(dqh));
+ memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
+ dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
+ dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
+ dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
+ if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
+ serrno = errno;
+ goto error;
+ }
+ }
+ grp = getgrnam(QUOTAGROUP);
+ fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
+ fchmod(newqf->fd, 0640);
+ for (id = 0; id <= maxid; id++) {
+ if ((quota_read(qf, &dqblk, id)) < 0)
+ break;
+ switch (newqf->wordsize) {
+ case 32:
+ if ((quota_write32(newqf, &dqblk, id)) < 0)
+ break;
+ continue;
+ case 64:
+ if ((quota_write64(newqf, &dqblk, id)) < 0)
+ break;
+ continue;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ }
+ if (id < maxid) {
+ serrno = errno;
+ goto error;
+ }
+ /*
+ * Update the passed in quotafile to reference the new file
+ * of the converted format size.
+ */
+ fd = qf->fd;
+ qf->fd = newqf->fd;
+ newqf->fd = fd;
+ qf->wordsize = newqf->wordsize;
+ quota_close(newqf);
+ return (0);
+error:
+ /* put back the original file */
+ (void) rename(newqf->qfname, qf->qfname);
+ quota_close(newqf);
+ errno = serrno;
+ return (-1);
+}
OpenPOWER on IntegriCloud