quotafile.c revision 188598
1/*- 2 * Copyright (c) 2008 Dag-Erling Co��dan Sm��rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: projects/quota64/lib/libutil/quotafile.c 188598 2009-02-13 19:56:59Z mckusick $ 28 */ 29 30#include <sys/types.h> 31#include <sys/endian.h> 32#include <sys/mount.h> 33#include <sys/stat.h> 34 35#include <ufs/ufs/quota.h> 36 37#include <errno.h> 38#include <fcntl.h> 39#include <fstab.h> 40#include <grp.h> 41#include <pwd.h> 42#include <libutil.h> 43#include <stdint.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <unistd.h> 48 49struct quotafile { 50 int fd; 51 int type; /* 32 or 64 */ 52}; 53 54static const char *qfextension[] = INITQFNAMES; 55 56struct quotafile * 57quota_open(const char *fn) 58{ 59 struct quotafile *qf; 60 struct dqhdr64 dqh; 61 int serrno; 62 63 if ((qf = calloc(1, sizeof(*qf))) == NULL) 64 return (NULL); 65 if ((qf->fd = open(fn, O_RDWR)) < 0) { 66 serrno = errno; 67 free(qf); 68 errno = serrno; 69 return (NULL); 70 } 71 qf->type = 32; 72 switch (read(qf->fd, &dqh, sizeof(dqh))) { 73 case -1: 74 serrno = errno; 75 close(qf->fd); 76 free(qf); 77 errno = serrno; 78 return (NULL); 79 case sizeof(dqh): 80 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { 81 /* no magic, assume 32 bits */ 82 qf->type = 32; 83 return (qf); 84 } 85 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || 86 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || 87 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { 88 /* correct magic, wrong version / lengths */ 89 close(qf->fd); 90 free(qf); 91 errno = EINVAL; 92 return (NULL); 93 } 94 qf->type = 64; 95 return (qf); 96 default: 97 qf->type = 32; 98 return (qf); 99 } 100 /* not reached */ 101} 102 103struct quotafile * 104quota_create(const char *fn) 105{ 106 struct quotafile *qf; 107 struct dqhdr64 dqh; 108 struct group *grp; 109 int serrno; 110 111 if ((qf = calloc(1, sizeof(*qf))) == NULL) 112 return (NULL); 113 if ((qf->fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) { 114 serrno = errno; 115 free(qf); 116 errno = serrno; 117 return (NULL); 118 } 119 qf->type = 64; 120 memset(&dqh, 0, sizeof(dqh)); 121 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 122 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 123 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 124 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 125 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 126 serrno = errno; 127 unlink(fn); 128 close(qf->fd); 129 free(qf); 130 errno = serrno; 131 return (NULL); 132 } 133 grp = getgrnam(QUOTAGROUP); 134 fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 135 fchmod(qf->fd, 0640); 136 return (qf); 137} 138 139void 140quota_close(struct quotafile *qf) 141{ 142 143 close(qf->fd); 144 free(qf); 145} 146 147static int 148quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 149{ 150 struct dqblk32 dqb32; 151 off_t off; 152 153 off = id * sizeof(struct dqblk32); 154 if (lseek(qf->fd, off, SEEK_SET) == -1) 155 return (-1); 156 switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 157 case 0: 158 memset(&dqb, 0, sizeof(*dqb)); 159 return (0); 160 case sizeof(dqb32): 161 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 162 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 163 dqb->dqb_curblocks = dqb32.dqb_curblocks; 164 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 165 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 166 dqb->dqb_curinodes = dqb32.dqb_curinodes; 167 dqb->dqb_btime = dqb32.dqb_btime; 168 dqb->dqb_itime = dqb32.dqb_itime; 169 return (0); 170 default: 171 return (-1); 172 } 173} 174 175static int 176quota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 177{ 178 struct dqblk64 dqb64; 179 off_t off; 180 181 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 182 if (lseek(qf->fd, off, SEEK_SET) == -1) 183 return (-1); 184 switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 185 case 0: 186 memset(&dqb, 0, sizeof(*dqb)); 187 return (0); 188 case sizeof(dqb64): 189 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 190 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 191 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 192 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 193 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 194 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 195 dqb->dqb_btime = be64toh(dqb64.dqb_btime); 196 dqb->dqb_itime = be64toh(dqb64.dqb_itime); 197 return (0); 198 default: 199 return (-1); 200 } 201} 202 203int 204quota_read(struct quotafile *qf, struct dqblk *dqb, int id) 205{ 206 207 switch (qf->type) { 208 case 32: 209 return quota_read32(qf, dqb, id); 210 case 64: 211 return quota_read64(qf, dqb, id); 212 default: 213 errno = EINVAL; 214 return (-1); 215 } 216 /* not reached */ 217} 218 219#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 220 221static int 222quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 223{ 224 struct dqblk32 dqb32; 225 off_t off; 226 227 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 228 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 229 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 230 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 231 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 232 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 233 dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 234 dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 235 236 off = id * sizeof(struct dqblk32); 237 if (lseek(qf->fd, off, SEEK_SET) == -1) 238 return (-1); 239 return (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)); 240} 241 242static int 243quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 244{ 245 struct dqblk64 dqb64; 246 off_t off; 247 248 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 249 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 250 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 251 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 252 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 253 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 254 dqb64.dqb_btime = htobe64(dqb->dqb_btime); 255 dqb64.dqb_itime = htobe64(dqb->dqb_itime); 256 257 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 258 if (lseek(qf->fd, off, SEEK_SET) == -1) 259 return (-1); 260 return (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)); 261} 262 263int 264quota_write(struct quotafile *qf, const struct dqblk *dqb, int id) 265{ 266 267 switch (qf->type) { 268 case 32: 269 return quota_write32(qf, dqb, id); 270 case 64: 271 return quota_write64(qf, dqb, id); 272 default: 273 errno = EINVAL; 274 return (-1); 275 } 276 /* not reached */ 277} 278 279/* 280 * Check to see if a particular quota is to be enabled. 281 */ 282int 283hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) 284{ 285 char *opt; 286 char *cp; 287 struct statfs sfb; 288 char buf[BUFSIZ]; 289 static char initname, usrname[100], grpname[100]; 290 291 if (!initname) { 292 (void)snprintf(usrname, sizeof(usrname), "%s%s", 293 qfextension[USRQUOTA], QUOTAFILENAME); 294 (void)snprintf(grpname, sizeof(grpname), "%s%s", 295 qfextension[GRPQUOTA], QUOTAFILENAME); 296 initname = 1; 297 } 298 strcpy(buf, fs->fs_mntops); 299 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 300 if ((cp = index(opt, '='))) 301 *cp++ = '\0'; 302 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 303 break; 304 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 305 break; 306 } 307 if (!opt) 308 return (0); 309 /* 310 * Ensure that the filesystem is mounted. 311 */ 312 if (statfs(fs->fs_file, &sfb) != 0 || 313 strcmp(fs->fs_file, sfb.f_mntonname)) { 314 return (0); 315 } 316 if (cp) { 317 strncpy(qfnamep, cp, qfbufsize); 318 } else { 319 (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, 320 QUOTAFILENAME, qfextension[type]); 321 } 322 return (1); 323} 324