1/*- 2 * Copyright (c) 2008 Dag-Erling Co��dan Sm��rgrav 3 * Copyright (c) 2008 Marshall Kirk McKusick 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 *
| 1/*- 2 * Copyright (c) 2008 Dag-Erling Co��dan Sm��rgrav 3 * Copyright (c) 2008 Marshall Kirk McKusick 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 *
|
28 * $FreeBSD: head/lib/libutil/quotafile.c 229403 2012-01-03 18:51:58Z ed $
| 28 * $FreeBSD: head/lib/libutil/quotafile.c 255007 2013-08-28 21:10:37Z jilles $
|
29 */ 30 31#include <sys/types.h> 32#include <sys/endian.h> 33#include <sys/mount.h> 34#include <sys/stat.h> 35 36#include <ufs/ufs/quota.h> 37 38#include <errno.h> 39#include <fcntl.h> 40#include <fstab.h> 41#include <grp.h> 42#include <pwd.h> 43#include <libutil.h> 44#include <stdint.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49 50struct quotafile { 51 int fd; /* -1 means using quotactl for access */ 52 int accmode; /* access mode */ 53 int wordsize; /* 32-bit or 64-bit limits */ 54 int quotatype; /* USRQUOTA or GRPQUOTA */ 55 dev_t dev; /* device */ 56 char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */ 57 char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */ 58}; 59 60static const char *qfextension[] = INITQFNAMES; 61 62/* 63 * Check to see if a particular quota is to be enabled. 64 */ 65static int 66hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) 67{ 68 char *opt; 69 char *cp; 70 struct statfs sfb; 71 char buf[BUFSIZ]; 72 static char initname, usrname[100], grpname[100]; 73 74 /* 75 * 1) we only need one of these 76 * 2) fstab may specify a different filename 77 */ 78 if (!initname) { 79 (void)snprintf(usrname, sizeof(usrname), "%s%s", 80 qfextension[USRQUOTA], QUOTAFILENAME); 81 (void)snprintf(grpname, sizeof(grpname), "%s%s", 82 qfextension[GRPQUOTA], QUOTAFILENAME); 83 initname = 1; 84 } 85 strcpy(buf, fs->fs_mntops); 86 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 87 if ((cp = strchr(opt, '='))) 88 *cp++ = '\0'; 89 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 90 break; 91 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 92 break; 93 } 94 if (!opt) 95 return (0); 96 /* 97 * Ensure that the filesystem is mounted. 98 */ 99 if (statfs(fs->fs_file, &sfb) != 0 || 100 strcmp(fs->fs_file, sfb.f_mntonname)) { 101 return (0); 102 } 103 if (cp) { 104 strncpy(qfnamep, cp, qfbufsize); 105 } else { 106 (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, 107 QUOTAFILENAME, qfextension[type]); 108 } 109 return (1); 110} 111 112struct quotafile * 113quota_open(struct fstab *fs, int quotatype, int openflags) 114{ 115 struct quotafile *qf; 116 struct dqhdr64 dqh; 117 struct group *grp; 118 struct stat st; 119 int qcmd, serrno; 120 121 if (strcmp(fs->fs_vfstype, "ufs")) 122 return (NULL); 123 if ((qf = calloc(1, sizeof(*qf))) == NULL) 124 return (NULL); 125 qf->fd = -1; 126 qf->quotatype = quotatype; 127 strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname)); 128 if (stat(qf->fsname, &st) != 0) 129 goto error; 130 qf->dev = st.st_dev; 131 serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); 132 qcmd = QCMD(Q_GETQUOTASIZE, quotatype); 133 if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) 134 return (qf); 135 if (serrno == 0) { 136 errno = EOPNOTSUPP; 137 goto error; 138 } 139 qf->accmode = openflags & O_ACCMODE;
| 29 */ 30 31#include <sys/types.h> 32#include <sys/endian.h> 33#include <sys/mount.h> 34#include <sys/stat.h> 35 36#include <ufs/ufs/quota.h> 37 38#include <errno.h> 39#include <fcntl.h> 40#include <fstab.h> 41#include <grp.h> 42#include <pwd.h> 43#include <libutil.h> 44#include <stdint.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49 50struct quotafile { 51 int fd; /* -1 means using quotactl for access */ 52 int accmode; /* access mode */ 53 int wordsize; /* 32-bit or 64-bit limits */ 54 int quotatype; /* USRQUOTA or GRPQUOTA */ 55 dev_t dev; /* device */ 56 char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */ 57 char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */ 58}; 59 60static const char *qfextension[] = INITQFNAMES; 61 62/* 63 * Check to see if a particular quota is to be enabled. 64 */ 65static int 66hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) 67{ 68 char *opt; 69 char *cp; 70 struct statfs sfb; 71 char buf[BUFSIZ]; 72 static char initname, usrname[100], grpname[100]; 73 74 /* 75 * 1) we only need one of these 76 * 2) fstab may specify a different filename 77 */ 78 if (!initname) { 79 (void)snprintf(usrname, sizeof(usrname), "%s%s", 80 qfextension[USRQUOTA], QUOTAFILENAME); 81 (void)snprintf(grpname, sizeof(grpname), "%s%s", 82 qfextension[GRPQUOTA], QUOTAFILENAME); 83 initname = 1; 84 } 85 strcpy(buf, fs->fs_mntops); 86 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 87 if ((cp = strchr(opt, '='))) 88 *cp++ = '\0'; 89 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 90 break; 91 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 92 break; 93 } 94 if (!opt) 95 return (0); 96 /* 97 * Ensure that the filesystem is mounted. 98 */ 99 if (statfs(fs->fs_file, &sfb) != 0 || 100 strcmp(fs->fs_file, sfb.f_mntonname)) { 101 return (0); 102 } 103 if (cp) { 104 strncpy(qfnamep, cp, qfbufsize); 105 } else { 106 (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, 107 QUOTAFILENAME, qfextension[type]); 108 } 109 return (1); 110} 111 112struct quotafile * 113quota_open(struct fstab *fs, int quotatype, int openflags) 114{ 115 struct quotafile *qf; 116 struct dqhdr64 dqh; 117 struct group *grp; 118 struct stat st; 119 int qcmd, serrno; 120 121 if (strcmp(fs->fs_vfstype, "ufs")) 122 return (NULL); 123 if ((qf = calloc(1, sizeof(*qf))) == NULL) 124 return (NULL); 125 qf->fd = -1; 126 qf->quotatype = quotatype; 127 strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname)); 128 if (stat(qf->fsname, &st) != 0) 129 goto error; 130 qf->dev = st.st_dev; 131 serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); 132 qcmd = QCMD(Q_GETQUOTASIZE, quotatype); 133 if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) 134 return (qf); 135 if (serrno == 0) { 136 errno = EOPNOTSUPP; 137 goto error; 138 } 139 qf->accmode = openflags & O_ACCMODE;
|
140 if ((qf->fd = open(qf->qfname, qf->accmode)) < 0 &&
| 140 if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
|
141 (openflags & O_CREAT) != O_CREAT) 142 goto error; 143 /* File open worked, so process it */ 144 if (qf->fd != -1) { 145 qf->wordsize = 32; 146 switch (read(qf->fd, &dqh, sizeof(dqh))) { 147 case -1: 148 goto error; 149 case sizeof(dqh): 150 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { 151 /* no magic, assume 32 bits */ 152 qf->wordsize = 32; 153 return (qf); 154 } 155 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || 156 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || 157 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { 158 /* correct magic, wrong version / lengths */ 159 errno = EINVAL; 160 goto error; 161 } 162 qf->wordsize = 64; 163 return (qf); 164 default: 165 qf->wordsize = 32; 166 return (qf); 167 } 168 /* not reached */ 169 } 170 /* open failed, but O_CREAT was specified, so create a new file */
| 141 (openflags & O_CREAT) != O_CREAT) 142 goto error; 143 /* File open worked, so process it */ 144 if (qf->fd != -1) { 145 qf->wordsize = 32; 146 switch (read(qf->fd, &dqh, sizeof(dqh))) { 147 case -1: 148 goto error; 149 case sizeof(dqh): 150 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { 151 /* no magic, assume 32 bits */ 152 qf->wordsize = 32; 153 return (qf); 154 } 155 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || 156 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || 157 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { 158 /* correct magic, wrong version / lengths */ 159 errno = EINVAL; 160 goto error; 161 } 162 qf->wordsize = 64; 163 return (qf); 164 default: 165 qf->wordsize = 32; 166 return (qf); 167 } 168 /* not reached */ 169 } 170 /* open failed, but O_CREAT was specified, so create a new file */
|
171 if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0)
| 171 if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) < 172 0)
|
172 goto error; 173 qf->wordsize = 64; 174 memset(&dqh, 0, sizeof(dqh)); 175 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 176 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 177 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 178 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 179 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 180 /* it was one we created ourselves */ 181 unlink(qf->qfname); 182 goto error; 183 } 184 grp = getgrnam(QUOTAGROUP); 185 fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 186 fchmod(qf->fd, 0640); 187 return (qf); 188error: 189 serrno = errno; 190 /* did we have an open file? */ 191 if (qf->fd != -1) 192 close(qf->fd); 193 free(qf); 194 errno = serrno; 195 return (NULL); 196} 197 198void 199quota_close(struct quotafile *qf) 200{ 201 202 if (qf->fd != -1) 203 close(qf->fd); 204 free(qf); 205} 206 207int 208quota_on(struct quotafile *qf) 209{ 210 int qcmd; 211 212 qcmd = QCMD(Q_QUOTAON, qf->quotatype); 213 return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); 214} 215 216int 217quota_off(struct quotafile *qf) 218{ 219 220 return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); 221} 222 223const char * 224quota_fsname(const struct quotafile *qf) 225{ 226 227 return (qf->fsname); 228} 229 230const char * 231quota_qfname(const struct quotafile *qf) 232{ 233 234 return (qf->qfname); 235} 236 237int 238quota_check_path(const struct quotafile *qf, const char *path) 239{ 240 struct stat st; 241 242 if (stat(path, &st) == -1) 243 return (-1); 244 return (st.st_dev == qf->dev); 245} 246 247int 248quota_maxid(struct quotafile *qf) 249{ 250 struct stat st; 251 int maxid; 252 253 if (stat(qf->qfname, &st) < 0) 254 return (0); 255 switch (qf->wordsize) { 256 case 32: 257 maxid = st.st_size / sizeof(struct dqblk32) - 1; 258 break; 259 case 64: 260 maxid = st.st_size / sizeof(struct dqblk64) - 2; 261 break; 262 default: 263 maxid = 0; 264 break; 265 } 266 return (maxid > 0 ? maxid : 0); 267} 268 269static int 270quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 271{ 272 struct dqblk32 dqb32; 273 off_t off; 274 275 off = id * sizeof(struct dqblk32); 276 if (lseek(qf->fd, off, SEEK_SET) == -1) 277 return (-1); 278 switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 279 case 0: 280 memset(dqb, 0, sizeof(*dqb)); 281 return (0); 282 case sizeof(dqb32): 283 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 284 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 285 dqb->dqb_curblocks = dqb32.dqb_curblocks; 286 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 287 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 288 dqb->dqb_curinodes = dqb32.dqb_curinodes; 289 dqb->dqb_btime = dqb32.dqb_btime; 290 dqb->dqb_itime = dqb32.dqb_itime; 291 return (0); 292 default: 293 return (-1); 294 } 295} 296 297static int 298quota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 299{ 300 struct dqblk64 dqb64; 301 off_t off; 302 303 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 304 if (lseek(qf->fd, off, SEEK_SET) == -1) 305 return (-1); 306 switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 307 case 0: 308 memset(dqb, 0, sizeof(*dqb)); 309 return (0); 310 case sizeof(dqb64): 311 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 312 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 313 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 314 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 315 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 316 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 317 dqb->dqb_btime = be64toh(dqb64.dqb_btime); 318 dqb->dqb_itime = be64toh(dqb64.dqb_itime); 319 return (0); 320 default: 321 return (-1); 322 } 323} 324 325int 326quota_read(struct quotafile *qf, struct dqblk *dqb, int id) 327{ 328 int qcmd; 329 330 if (qf->fd == -1) { 331 qcmd = QCMD(Q_GETQUOTA, qf->quotatype); 332 return (quotactl(qf->fsname, qcmd, id, dqb)); 333 } 334 switch (qf->wordsize) { 335 case 32: 336 return (quota_read32(qf, dqb, id)); 337 case 64: 338 return (quota_read64(qf, dqb, id)); 339 default: 340 errno = EINVAL; 341 return (-1); 342 } 343 /* not reached */ 344} 345 346#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 347 348static int 349quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 350{ 351 struct dqblk32 dqb32; 352 off_t off; 353 354 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 355 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 356 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 357 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 358 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 359 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 360 dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 361 dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 362 363 off = id * sizeof(struct dqblk32); 364 if (lseek(qf->fd, off, SEEK_SET) == -1) 365 return (-1); 366 if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) 367 return (0); 368 return (-1); 369} 370 371static int 372quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 373{ 374 struct dqblk64 dqb64; 375 off_t off; 376 377 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 378 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 379 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 380 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 381 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 382 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 383 dqb64.dqb_btime = htobe64(dqb->dqb_btime); 384 dqb64.dqb_itime = htobe64(dqb->dqb_itime); 385 386 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 387 if (lseek(qf->fd, off, SEEK_SET) == -1) 388 return (-1); 389 if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) 390 return (0); 391 return (-1); 392} 393 394int 395quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) 396{ 397 struct dqblk dqbuf; 398 int qcmd; 399 400 if (qf->fd == -1) { 401 qcmd = QCMD(Q_SETUSE, qf->quotatype); 402 return (quotactl(qf->fsname, qcmd, id, dqb)); 403 } 404 /* 405 * Have to do read-modify-write of quota in file. 406 */ 407 if ((qf->accmode & O_RDWR) != O_RDWR) { 408 errno = EBADF; 409 return (-1); 410 } 411 if (quota_read(qf, &dqbuf, id) != 0) 412 return (-1); 413 /* 414 * Reset time limit if have a soft limit and were 415 * previously under it, but are now over it. 416 */ 417 if (dqbuf.dqb_bsoftlimit && id != 0 && 418 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 419 dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) 420 dqbuf.dqb_btime = 0; 421 if (dqbuf.dqb_isoftlimit && id != 0 && 422 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 423 dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) 424 dqbuf.dqb_itime = 0; 425 dqbuf.dqb_curinodes = dqb->dqb_curinodes; 426 dqbuf.dqb_curblocks = dqb->dqb_curblocks; 427 /* 428 * Write it back. 429 */ 430 switch (qf->wordsize) { 431 case 32: 432 return (quota_write32(qf, &dqbuf, id)); 433 case 64: 434 return (quota_write64(qf, &dqbuf, id)); 435 default: 436 errno = EINVAL; 437 return (-1); 438 } 439 /* not reached */ 440} 441 442int 443quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) 444{ 445 struct dqblk dqbuf; 446 int qcmd; 447 448 if (qf->fd == -1) { 449 qcmd = QCMD(Q_SETQUOTA, qf->quotatype); 450 return (quotactl(qf->fsname, qcmd, id, dqb)); 451 } 452 /* 453 * Have to do read-modify-write of quota in file. 454 */ 455 if ((qf->accmode & O_RDWR) != O_RDWR) { 456 errno = EBADF; 457 return (-1); 458 } 459 if (quota_read(qf, &dqbuf, id) != 0) 460 return (-1); 461 /* 462 * Reset time limit if have a soft limit and were 463 * previously under it, but are now over it 464 * or if there previously was no soft limit, but 465 * now have one and are over it. 466 */ 467 if (dqbuf.dqb_bsoftlimit && id != 0 && 468 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 469 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 470 dqb->dqb_btime = 0; 471 if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && 472 dqb->dqb_bsoftlimit > 0 && 473 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 474 dqb->dqb_btime = 0; 475 if (dqbuf.dqb_isoftlimit && id != 0 && 476 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 477 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 478 dqb->dqb_itime = 0; 479 if (dqbuf.dqb_isoftlimit == 0 && id !=0 && 480 dqb->dqb_isoftlimit > 0 && 481 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 482 dqb->dqb_itime = 0; 483 dqb->dqb_curinodes = dqbuf.dqb_curinodes; 484 dqb->dqb_curblocks = dqbuf.dqb_curblocks; 485 /* 486 * Write it back. 487 */ 488 switch (qf->wordsize) { 489 case 32: 490 return (quota_write32(qf, dqb, id)); 491 case 64: 492 return (quota_write64(qf, dqb, id)); 493 default: 494 errno = EINVAL; 495 return (-1); 496 } 497 /* not reached */ 498} 499 500/* 501 * Convert a quota file from one format to another. 502 */ 503int 504quota_convert(struct quotafile *qf, int wordsize) 505{ 506 struct quotafile *newqf; 507 struct dqhdr64 dqh; 508 struct dqblk dqblk; 509 struct group *grp; 510 int serrno, maxid, id, fd; 511 512 /* 513 * Quotas must not be active and quotafile must be open 514 * for reading and writing. 515 */ 516 if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { 517 errno = EBADF; 518 return (-1); 519 } 520 if ((wordsize != 32 && wordsize != 64) || 521 wordsize == qf->wordsize) { 522 errno = EINVAL; 523 return (-1); 524 } 525 maxid = quota_maxid(qf); 526 if ((newqf = calloc(1, sizeof(*qf))) == NULL) { 527 errno = ENOMEM; 528 return (-1); 529 } 530 *newqf = *qf; 531 snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, 532 qf->wordsize); 533 if (rename(qf->qfname, newqf->qfname) < 0) { 534 free(newqf); 535 return (-1); 536 }
| 173 goto error; 174 qf->wordsize = 64; 175 memset(&dqh, 0, sizeof(dqh)); 176 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 177 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 178 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 179 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 180 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 181 /* it was one we created ourselves */ 182 unlink(qf->qfname); 183 goto error; 184 } 185 grp = getgrnam(QUOTAGROUP); 186 fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 187 fchmod(qf->fd, 0640); 188 return (qf); 189error: 190 serrno = errno; 191 /* did we have an open file? */ 192 if (qf->fd != -1) 193 close(qf->fd); 194 free(qf); 195 errno = serrno; 196 return (NULL); 197} 198 199void 200quota_close(struct quotafile *qf) 201{ 202 203 if (qf->fd != -1) 204 close(qf->fd); 205 free(qf); 206} 207 208int 209quota_on(struct quotafile *qf) 210{ 211 int qcmd; 212 213 qcmd = QCMD(Q_QUOTAON, qf->quotatype); 214 return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); 215} 216 217int 218quota_off(struct quotafile *qf) 219{ 220 221 return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); 222} 223 224const char * 225quota_fsname(const struct quotafile *qf) 226{ 227 228 return (qf->fsname); 229} 230 231const char * 232quota_qfname(const struct quotafile *qf) 233{ 234 235 return (qf->qfname); 236} 237 238int 239quota_check_path(const struct quotafile *qf, const char *path) 240{ 241 struct stat st; 242 243 if (stat(path, &st) == -1) 244 return (-1); 245 return (st.st_dev == qf->dev); 246} 247 248int 249quota_maxid(struct quotafile *qf) 250{ 251 struct stat st; 252 int maxid; 253 254 if (stat(qf->qfname, &st) < 0) 255 return (0); 256 switch (qf->wordsize) { 257 case 32: 258 maxid = st.st_size / sizeof(struct dqblk32) - 1; 259 break; 260 case 64: 261 maxid = st.st_size / sizeof(struct dqblk64) - 2; 262 break; 263 default: 264 maxid = 0; 265 break; 266 } 267 return (maxid > 0 ? maxid : 0); 268} 269 270static int 271quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 272{ 273 struct dqblk32 dqb32; 274 off_t off; 275 276 off = id * sizeof(struct dqblk32); 277 if (lseek(qf->fd, off, SEEK_SET) == -1) 278 return (-1); 279 switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 280 case 0: 281 memset(dqb, 0, sizeof(*dqb)); 282 return (0); 283 case sizeof(dqb32): 284 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 285 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 286 dqb->dqb_curblocks = dqb32.dqb_curblocks; 287 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 288 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 289 dqb->dqb_curinodes = dqb32.dqb_curinodes; 290 dqb->dqb_btime = dqb32.dqb_btime; 291 dqb->dqb_itime = dqb32.dqb_itime; 292 return (0); 293 default: 294 return (-1); 295 } 296} 297 298static int 299quota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 300{ 301 struct dqblk64 dqb64; 302 off_t off; 303 304 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 305 if (lseek(qf->fd, off, SEEK_SET) == -1) 306 return (-1); 307 switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 308 case 0: 309 memset(dqb, 0, sizeof(*dqb)); 310 return (0); 311 case sizeof(dqb64): 312 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 313 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 314 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 315 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 316 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 317 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 318 dqb->dqb_btime = be64toh(dqb64.dqb_btime); 319 dqb->dqb_itime = be64toh(dqb64.dqb_itime); 320 return (0); 321 default: 322 return (-1); 323 } 324} 325 326int 327quota_read(struct quotafile *qf, struct dqblk *dqb, int id) 328{ 329 int qcmd; 330 331 if (qf->fd == -1) { 332 qcmd = QCMD(Q_GETQUOTA, qf->quotatype); 333 return (quotactl(qf->fsname, qcmd, id, dqb)); 334 } 335 switch (qf->wordsize) { 336 case 32: 337 return (quota_read32(qf, dqb, id)); 338 case 64: 339 return (quota_read64(qf, dqb, id)); 340 default: 341 errno = EINVAL; 342 return (-1); 343 } 344 /* not reached */ 345} 346 347#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 348 349static int 350quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 351{ 352 struct dqblk32 dqb32; 353 off_t off; 354 355 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 356 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 357 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 358 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 359 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 360 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 361 dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 362 dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 363 364 off = id * sizeof(struct dqblk32); 365 if (lseek(qf->fd, off, SEEK_SET) == -1) 366 return (-1); 367 if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) 368 return (0); 369 return (-1); 370} 371 372static int 373quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 374{ 375 struct dqblk64 dqb64; 376 off_t off; 377 378 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 379 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 380 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 381 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 382 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 383 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 384 dqb64.dqb_btime = htobe64(dqb->dqb_btime); 385 dqb64.dqb_itime = htobe64(dqb->dqb_itime); 386 387 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 388 if (lseek(qf->fd, off, SEEK_SET) == -1) 389 return (-1); 390 if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) 391 return (0); 392 return (-1); 393} 394 395int 396quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) 397{ 398 struct dqblk dqbuf; 399 int qcmd; 400 401 if (qf->fd == -1) { 402 qcmd = QCMD(Q_SETUSE, qf->quotatype); 403 return (quotactl(qf->fsname, qcmd, id, dqb)); 404 } 405 /* 406 * Have to do read-modify-write of quota in file. 407 */ 408 if ((qf->accmode & O_RDWR) != O_RDWR) { 409 errno = EBADF; 410 return (-1); 411 } 412 if (quota_read(qf, &dqbuf, id) != 0) 413 return (-1); 414 /* 415 * Reset time limit if have a soft limit and were 416 * previously under it, but are now over it. 417 */ 418 if (dqbuf.dqb_bsoftlimit && id != 0 && 419 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 420 dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) 421 dqbuf.dqb_btime = 0; 422 if (dqbuf.dqb_isoftlimit && id != 0 && 423 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 424 dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) 425 dqbuf.dqb_itime = 0; 426 dqbuf.dqb_curinodes = dqb->dqb_curinodes; 427 dqbuf.dqb_curblocks = dqb->dqb_curblocks; 428 /* 429 * Write it back. 430 */ 431 switch (qf->wordsize) { 432 case 32: 433 return (quota_write32(qf, &dqbuf, id)); 434 case 64: 435 return (quota_write64(qf, &dqbuf, id)); 436 default: 437 errno = EINVAL; 438 return (-1); 439 } 440 /* not reached */ 441} 442 443int 444quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) 445{ 446 struct dqblk dqbuf; 447 int qcmd; 448 449 if (qf->fd == -1) { 450 qcmd = QCMD(Q_SETQUOTA, qf->quotatype); 451 return (quotactl(qf->fsname, qcmd, id, dqb)); 452 } 453 /* 454 * Have to do read-modify-write of quota in file. 455 */ 456 if ((qf->accmode & O_RDWR) != O_RDWR) { 457 errno = EBADF; 458 return (-1); 459 } 460 if (quota_read(qf, &dqbuf, id) != 0) 461 return (-1); 462 /* 463 * Reset time limit if have a soft limit and were 464 * previously under it, but are now over it 465 * or if there previously was no soft limit, but 466 * now have one and are over it. 467 */ 468 if (dqbuf.dqb_bsoftlimit && id != 0 && 469 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 470 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 471 dqb->dqb_btime = 0; 472 if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && 473 dqb->dqb_bsoftlimit > 0 && 474 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 475 dqb->dqb_btime = 0; 476 if (dqbuf.dqb_isoftlimit && id != 0 && 477 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 478 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 479 dqb->dqb_itime = 0; 480 if (dqbuf.dqb_isoftlimit == 0 && id !=0 && 481 dqb->dqb_isoftlimit > 0 && 482 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 483 dqb->dqb_itime = 0; 484 dqb->dqb_curinodes = dqbuf.dqb_curinodes; 485 dqb->dqb_curblocks = dqbuf.dqb_curblocks; 486 /* 487 * Write it back. 488 */ 489 switch (qf->wordsize) { 490 case 32: 491 return (quota_write32(qf, dqb, id)); 492 case 64: 493 return (quota_write64(qf, dqb, id)); 494 default: 495 errno = EINVAL; 496 return (-1); 497 } 498 /* not reached */ 499} 500 501/* 502 * Convert a quota file from one format to another. 503 */ 504int 505quota_convert(struct quotafile *qf, int wordsize) 506{ 507 struct quotafile *newqf; 508 struct dqhdr64 dqh; 509 struct dqblk dqblk; 510 struct group *grp; 511 int serrno, maxid, id, fd; 512 513 /* 514 * Quotas must not be active and quotafile must be open 515 * for reading and writing. 516 */ 517 if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { 518 errno = EBADF; 519 return (-1); 520 } 521 if ((wordsize != 32 && wordsize != 64) || 522 wordsize == qf->wordsize) { 523 errno = EINVAL; 524 return (-1); 525 } 526 maxid = quota_maxid(qf); 527 if ((newqf = calloc(1, sizeof(*qf))) == NULL) { 528 errno = ENOMEM; 529 return (-1); 530 } 531 *newqf = *qf; 532 snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, 533 qf->wordsize); 534 if (rename(qf->qfname, newqf->qfname) < 0) { 535 free(newqf); 536 return (-1); 537 }
|
537 if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
| 538 if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 539 0)) < 0) {
|
538 serrno = errno; 539 goto error; 540 } 541 newqf->wordsize = wordsize; 542 if (wordsize == 64) { 543 memset(&dqh, 0, sizeof(dqh)); 544 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 545 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 546 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 547 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 548 if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 549 serrno = errno; 550 goto error; 551 } 552 } 553 grp = getgrnam(QUOTAGROUP); 554 fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); 555 fchmod(newqf->fd, 0640); 556 for (id = 0; id <= maxid; id++) { 557 if ((quota_read(qf, &dqblk, id)) < 0) 558 break; 559 switch (newqf->wordsize) { 560 case 32: 561 if ((quota_write32(newqf, &dqblk, id)) < 0) 562 break; 563 continue; 564 case 64: 565 if ((quota_write64(newqf, &dqblk, id)) < 0) 566 break; 567 continue; 568 default: 569 errno = EINVAL; 570 break; 571 } 572 } 573 if (id < maxid) { 574 serrno = errno; 575 goto error; 576 } 577 /* 578 * Update the passed in quotafile to reference the new file 579 * of the converted format size. 580 */ 581 fd = qf->fd; 582 qf->fd = newqf->fd; 583 newqf->fd = fd; 584 qf->wordsize = newqf->wordsize; 585 quota_close(newqf); 586 return (0); 587error: 588 /* put back the original file */ 589 (void) rename(newqf->qfname, qf->qfname); 590 quota_close(newqf); 591 errno = serrno; 592 return (-1); 593}
| 540 serrno = errno; 541 goto error; 542 } 543 newqf->wordsize = wordsize; 544 if (wordsize == 64) { 545 memset(&dqh, 0, sizeof(dqh)); 546 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 547 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 548 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 549 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 550 if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 551 serrno = errno; 552 goto error; 553 } 554 } 555 grp = getgrnam(QUOTAGROUP); 556 fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); 557 fchmod(newqf->fd, 0640); 558 for (id = 0; id <= maxid; id++) { 559 if ((quota_read(qf, &dqblk, id)) < 0) 560 break; 561 switch (newqf->wordsize) { 562 case 32: 563 if ((quota_write32(newqf, &dqblk, id)) < 0) 564 break; 565 continue; 566 case 64: 567 if ((quota_write64(newqf, &dqblk, id)) < 0) 568 break; 569 continue; 570 default: 571 errno = EINVAL; 572 break; 573 } 574 } 575 if (id < maxid) { 576 serrno = errno; 577 goto error; 578 } 579 /* 580 * Update the passed in quotafile to reference the new file 581 * of the converted format size. 582 */ 583 fd = qf->fd; 584 qf->fd = newqf->fd; 585 newqf->fd = fd; 586 qf->wordsize = newqf->wordsize; 587 quota_close(newqf); 588 return (0); 589error: 590 /* put back the original file */ 591 (void) rename(newqf->qfname, qf->qfname); 592 quota_close(newqf); 593 errno = serrno; 594 return (-1); 595}
|