diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index d7bd6dc145a5..6b489d3b9d3c 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -60,8 +60,12 @@ MLINKS+=pidfile.3 pidfile_open.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.3 \ + quotafile.3 quota_write_limits.3 \ + quotafile.3 quota_write_usage.3 \ quotafile.3 quota_close.3 .include diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index afd5db6d2285..2190a532f793 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -144,10 +144,13 @@ int pidfile_remove(struct pidfh *pfh); struct quotafile; struct fstab; struct quotafile *quota_open(struct fstab *, int, int); -void quota_close(struct quotafile *); +const char *quota_fsname(const struct quotafile *); +const char *quota_qfname(const 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); +void quota_close(struct quotafile *); #endif __END_DECLS diff --git a/lib/libutil/quotafile.3 b/lib/libutil/quotafile.3 index 07a08a20c184..4762bf92558c 100644 --- a/lib/libutil/quotafile.3 +++ b/lib/libutil/quotafile.3 @@ -25,11 +25,14 @@ .\" .\" $FreeBSD$ .\" -.Dd February 14, 2009 +.Dd September 26, 2009 .Dt QUOTAFILE 3 .Os .Sh NAME .Nm quota_open +.Nm quota_fsname +.Nm quota_qfname +.Nm quota_check_path .Nm quota_read .Nm quota_write_limits .Nm quota_write_usage @@ -38,11 +41,19 @@ .Sh LIBRARY .Lb libutil .Sh SYNOPSIS +.In sys/param.h +.In sys/mount.h .In ufs/ufs/quota.h .In libutil.h .In fstab.h .Ft "struct quotafile *" .Fn quota_open "struct fstab *fs" "int quotatype" "int openflags" +.Ft "const char *" +.Fn quota_fsname "const struct quotafile *qf" +.Ft "const char *" +.Fn quota_qfname "const struct quotafile *qf" +.Ft int +.Fn quota_check_path "const struct quotafile *qf" "const char *path" .Ft int .Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int id" .Ft int @@ -84,12 +95,42 @@ if the quotas are just to be read, or 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. +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_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_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_read -function reads the quota from the filesystem and quota type referenced by +function reads the quota from the filesystem and quota type referenced +by .Va qf for the user (or group) specified by .Va id @@ -127,6 +168,9 @@ The function closes any open file descriptors and frees any storage associated with the filesystem and quota type referenced by .Va qf . +.Sh IMPLEMENTATION NOTES +If the underlying quota file is in 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 @@ -137,7 +181,15 @@ If the filesystem has no quotas, or access permission is denied .Dv NULL is returned and .Va errno -is set to indicate the cause of failure. +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 , @@ -146,11 +198,10 @@ The and .Fn quota_close functions return zero on success. -On error they return -.Dv -1 +On error they return\~-1 and set .Va errno -to indicate the cause of failure. +to indicate the error. .Sh SEE ALSO .Xr quotactl 2 , .Xr quota.user 5 , diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c index 98f6f7c2d683..6dcb6979fb12 100644 --- a/lib/libutil/quotafile.c +++ b/lib/libutil/quotafile.c @@ -49,8 +49,10 @@ 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 */ }; @@ -59,6 +61,7 @@ static const char *qfextension[] = INITQFNAMES; /* * Check to see if a particular quota is to be enabled. + * XXX merge into quota_open */ static int hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) @@ -69,6 +72,11 @@ hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) char buf[BUFSIZ]; static char initname, usrname[100], grpname[100]; + /* + * XXX + * 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); @@ -109,12 +117,17 @@ 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 ((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; qcmd = QCMD(Q_GETQUOTA, quotatype); if (quotactl(fs->fs_file, qcmd, 0, &dqh) == 0) { qf->wordsize = 64; @@ -122,27 +135,19 @@ quota_open(struct fstab *fs, int quotatype, int openflags) return (qf); } if (!hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname))) { - free(qf); errno = EOPNOTSUPP; - return (NULL); - } - if ((qf->fd = open(qf->qfname, openflags & O_ACCMODE)) < 0 && - (openflags & O_CREAT) == 0) { - serrno = errno; - free(qf); - errno = serrno; - return (NULL); + 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: - serrno = errno; - close(qf->fd); - free(qf); - errno = serrno; - return (NULL); + goto error; case sizeof(dqh): if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { /* no magic, assume 32 bits */ @@ -153,10 +158,8 @@ quota_open(struct fstab *fs, int quotatype, int openflags) be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { /* correct magic, wrong version / lengths */ - close(qf->fd); - free(qf); errno = EINVAL; - return (NULL); + goto error; } qf->wordsize = 64; return (qf); @@ -166,31 +169,33 @@ quota_open(struct fstab *fs, int quotatype, int openflags) } /* not reached */ } - /* Open failed above, but O_CREAT specified, so create a new file */ - if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) { - serrno = errno; - free(qf); - errno = serrno; - return (NULL); - } + /* 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)) { - serrno = errno; - unlink(qf->qfname); - close(qf->fd); - free(qf); - errno = serrno; - return (NULL); - } + if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) + 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) { + /* was it one we created ourselves? */ + if ((openflags & O_CREAT) == O_CREAT) + unlink(qf->qfname); + close(qf->fd); + } + free(qf); + errno = serrno; + return (NULL); } void @@ -202,6 +207,30 @@ quota_close(struct quotafile *qf) free(qf); } +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); +} + static int quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) { @@ -333,6 +362,10 @@ quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) struct dqblk dqbuf; int qcmd; + if ((qf->accmode & O_RDWR) != O_RDWR) { + errno = EBADF; + return (-1); + } if (qf->fd == -1) { qcmd = QCMD(Q_SETUSE, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb)); @@ -377,6 +410,10 @@ quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) struct dqblk dqbuf; int qcmd; + if ((qf->accmode & O_RDWR) != O_RDWR) { + errno = EBADF; + return (-1); + } if (qf->fd == -1) { qcmd = QCMD(Q_SETQUOTA, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb));