1 1.10 hannken /* $NetBSD: quota_oldfiles.c,v 1.10 2022/04/26 15:36:42 hannken Exp $ */ 2 1.1 dholland 3 1.1 dholland /* 4 1.1 dholland * Copyright (c) 1980, 1990, 1993 5 1.1 dholland * The Regents of the University of California. All rights reserved. 6 1.1 dholland * 7 1.1 dholland * This code is derived from software contributed to Berkeley by 8 1.1 dholland * Robert Elz at The University of Melbourne. 9 1.1 dholland * 10 1.1 dholland * Redistribution and use in source and binary forms, with or without 11 1.1 dholland * modification, are permitted provided that the following conditions 12 1.1 dholland * are met: 13 1.1 dholland * 1. Redistributions of source code must retain the above copyright 14 1.1 dholland * notice, this list of conditions and the following disclaimer. 15 1.1 dholland * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 dholland * notice, this list of conditions and the following disclaimer in the 17 1.1 dholland * documentation and/or other materials provided with the distribution. 18 1.1 dholland * 3. Neither the name of the University nor the names of its contributors 19 1.1 dholland * may be used to endorse or promote products derived from this software 20 1.1 dholland * without specific prior written permission. 21 1.1 dholland * 22 1.1 dholland * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 dholland * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 dholland * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 dholland * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 dholland * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 dholland * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 dholland * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 dholland * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 dholland * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 dholland * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 dholland * SUCH DAMAGE. 33 1.1 dholland */ 34 1.1 dholland 35 1.8 dholland #include <sys/cdefs.h> 36 1.10 hannken __RCSID("$NetBSD: quota_oldfiles.c,v 1.10 2022/04/26 15:36:42 hannken Exp $"); 37 1.8 dholland 38 1.1 dholland #include <sys/types.h> 39 1.1 dholland #include <sys/stat.h> 40 1.1 dholland #include <stdio.h> 41 1.1 dholland #include <stdlib.h> 42 1.1 dholland #include <string.h> 43 1.1 dholland #include <unistd.h> 44 1.1 dholland #include <fcntl.h> 45 1.1 dholland #include <limits.h> 46 1.1 dholland #include <fstab.h> 47 1.1 dholland #include <errno.h> 48 1.1 dholland #include <err.h> 49 1.1 dholland 50 1.1 dholland #include <ufs/ufs/quota1.h> 51 1.1 dholland 52 1.1 dholland #include <quota.h> 53 1.1 dholland #include "quotapvt.h" 54 1.1 dholland 55 1.3 dholland struct oldfiles_fstabentry { 56 1.3 dholland char *ofe_mountpoint; 57 1.3 dholland int ofe_hasuserquota; 58 1.3 dholland int ofe_hasgroupquota; 59 1.3 dholland char *ofe_userquotafile; 60 1.3 dholland char *ofe_groupquotafile; 61 1.3 dholland }; 62 1.3 dholland 63 1.1 dholland struct oldfiles_quotacursor { 64 1.1 dholland unsigned oqc_doingusers; 65 1.1 dholland unsigned oqc_doinggroups; 66 1.1 dholland 67 1.1 dholland unsigned oqc_numusers; 68 1.1 dholland unsigned oqc_numgroups; 69 1.1 dholland 70 1.1 dholland unsigned oqc_didusers; 71 1.1 dholland unsigned oqc_didgroups; 72 1.1 dholland unsigned oqc_diddefault; 73 1.1 dholland unsigned oqc_pos; 74 1.1 dholland unsigned oqc_didblocks; 75 1.1 dholland }; 76 1.1 dholland 77 1.3 dholland static struct oldfiles_fstabentry *__quota_oldfiles_fstab; 78 1.3 dholland static unsigned __quota_oldfiles_numfstab; 79 1.3 dholland static unsigned __quota_oldfiles_maxfstab; 80 1.3 dholland static int __quota_oldfiles_fstab_loaded; 81 1.3 dholland 82 1.3 dholland static const struct oldfiles_fstabentry * 83 1.3 dholland __quota_oldfiles_find_fstabentry(const char *mountpoint) 84 1.3 dholland { 85 1.3 dholland unsigned i; 86 1.3 dholland 87 1.3 dholland for (i = 0; i < __quota_oldfiles_numfstab; i++) { 88 1.3 dholland if (!strcmp(mountpoint, 89 1.3 dholland __quota_oldfiles_fstab[i].ofe_mountpoint)) { 90 1.3 dholland return &__quota_oldfiles_fstab[i]; 91 1.3 dholland } 92 1.3 dholland } 93 1.3 dholland return NULL; 94 1.3 dholland } 95 1.3 dholland 96 1.3 dholland static int 97 1.3 dholland __quota_oldfiles_add_fstabentry(struct oldfiles_fstabentry *ofe) 98 1.3 dholland { 99 1.3 dholland unsigned newmax; 100 1.3 dholland struct oldfiles_fstabentry *newptr; 101 1.3 dholland 102 1.3 dholland if (__quota_oldfiles_numfstab + 1 >= __quota_oldfiles_maxfstab) { 103 1.3 dholland if (__quota_oldfiles_maxfstab == 0) { 104 1.3 dholland newmax = 4; 105 1.3 dholland } else { 106 1.3 dholland newmax = __quota_oldfiles_maxfstab * 2; 107 1.3 dholland } 108 1.3 dholland newptr = realloc(__quota_oldfiles_fstab, 109 1.3 dholland newmax * sizeof(__quota_oldfiles_fstab[0])); 110 1.3 dholland if (newptr == NULL) { 111 1.3 dholland return -1; 112 1.3 dholland } 113 1.3 dholland __quota_oldfiles_maxfstab = newmax; 114 1.3 dholland __quota_oldfiles_fstab = newptr; 115 1.3 dholland } 116 1.3 dholland 117 1.3 dholland __quota_oldfiles_fstab[__quota_oldfiles_numfstab++] = *ofe; 118 1.3 dholland return 0; 119 1.3 dholland } 120 1.3 dholland 121 1.3 dholland static int 122 1.3 dholland __quota_oldfiles_fill_fstabentry(const struct fstab *fs, 123 1.3 dholland struct oldfiles_fstabentry *ofe) 124 1.3 dholland { 125 1.4 dholland char buf[256]; 126 1.3 dholland char *opt, *state, *s; 127 1.3 dholland int serrno; 128 1.3 dholland int ret = 0; 129 1.3 dholland 130 1.3 dholland /* 131 1.3 dholland * Inspect the mount options to find the quota files. 132 1.3 dholland * XXX this info should be gotten from the kernel. 133 1.3 dholland * 134 1.3 dholland * The options are: 135 1.3 dholland * userquota[=path] enable user quotas 136 1.3 dholland * groupquota[=path] enable group quotas 137 1.3 dholland */ 138 1.3 dholland 139 1.3 dholland ofe->ofe_mountpoint = NULL; 140 1.3 dholland ofe->ofe_hasuserquota = ofe->ofe_hasgroupquota = 0; 141 1.3 dholland ofe->ofe_userquotafile = ofe->ofe_groupquotafile = NULL; 142 1.3 dholland 143 1.3 dholland strlcpy(buf, fs->fs_mntops, sizeof(buf)); 144 1.3 dholland for (opt = strtok_r(buf, ",", &state); 145 1.3 dholland opt != NULL; 146 1.3 dholland opt = strtok_r(NULL, ",", &state)) { 147 1.3 dholland s = strchr(opt, '='); 148 1.3 dholland if (s != NULL) { 149 1.3 dholland *(s++) = '\0'; 150 1.3 dholland } 151 1.3 dholland if (!strcmp(opt, "userquota")) { 152 1.3 dholland ret = 1; 153 1.3 dholland ofe->ofe_hasuserquota = 1; 154 1.3 dholland if (s != NULL) { 155 1.3 dholland ofe->ofe_userquotafile = strdup(s); 156 1.3 dholland if (ofe->ofe_userquotafile == NULL) { 157 1.3 dholland goto fail; 158 1.3 dholland } 159 1.3 dholland } 160 1.3 dholland } else if (!strcmp(opt, "groupquota")) { 161 1.3 dholland ret = 1; 162 1.3 dholland ofe->ofe_hasgroupquota = 1; 163 1.3 dholland if (s != NULL) { 164 1.3 dholland ofe->ofe_groupquotafile = strdup(s); 165 1.3 dholland if (ofe->ofe_groupquotafile == NULL) { 166 1.3 dholland goto fail; 167 1.3 dholland } 168 1.3 dholland } 169 1.3 dholland } 170 1.3 dholland } 171 1.3 dholland 172 1.3 dholland if (ret == 1) { 173 1.3 dholland ofe->ofe_mountpoint = strdup(fs->fs_file); 174 1.3 dholland if (ofe->ofe_mountpoint == NULL) { 175 1.3 dholland goto fail; 176 1.3 dholland } 177 1.3 dholland } 178 1.3 dholland 179 1.3 dholland return ret; 180 1.3 dholland 181 1.3 dholland fail: 182 1.3 dholland serrno = errno; 183 1.3 dholland if (ofe->ofe_mountpoint != NULL) { 184 1.3 dholland free(ofe->ofe_mountpoint); 185 1.3 dholland } 186 1.3 dholland if (ofe->ofe_groupquotafile != NULL) { 187 1.3 dholland free(ofe->ofe_groupquotafile); 188 1.3 dholland } 189 1.3 dholland if (ofe->ofe_userquotafile != NULL) { 190 1.3 dholland free(ofe->ofe_userquotafile); 191 1.3 dholland } 192 1.3 dholland errno = serrno; 193 1.3 dholland return -1; 194 1.3 dholland } 195 1.3 dholland 196 1.3 dholland void 197 1.3 dholland __quota_oldfiles_load_fstab(void) 198 1.3 dholland { 199 1.3 dholland struct oldfiles_fstabentry ofe; 200 1.3 dholland struct fstab *fs; 201 1.3 dholland int result; 202 1.3 dholland 203 1.3 dholland if (__quota_oldfiles_fstab_loaded) { 204 1.3 dholland return; 205 1.3 dholland } 206 1.3 dholland 207 1.3 dholland /* 208 1.9 njoly * Check if fstab file exists before trying to parse it. 209 1.9 njoly * Avoid warnings from {get,set}fsent() if missing. 210 1.9 njoly */ 211 1.9 njoly if (access(_PATH_FSTAB, F_OK) == -1 && errno == ENOENT) 212 1.9 njoly return; 213 1.9 njoly 214 1.9 njoly /* 215 1.3 dholland * XXX: should be able to handle ext2fs quota1 files too 216 1.3 dholland * 217 1.3 dholland * XXX: should use getfsent_r(), but there isn't one. 218 1.3 dholland */ 219 1.3 dholland setfsent(); 220 1.3 dholland while ((fs = getfsent()) != NULL) { 221 1.3 dholland if (!strcmp(fs->fs_vfstype, "ffs") || 222 1.3 dholland !strcmp(fs->fs_vfstype, "lfs")) { 223 1.3 dholland result = __quota_oldfiles_fill_fstabentry(fs, &ofe); 224 1.3 dholland if (result == -1) { 225 1.3 dholland goto failed; 226 1.3 dholland } 227 1.3 dholland if (result == 0) { 228 1.3 dholland continue; 229 1.3 dholland } 230 1.3 dholland if (__quota_oldfiles_add_fstabentry(&ofe)) { 231 1.3 dholland goto failed; 232 1.3 dholland } 233 1.3 dholland } 234 1.3 dholland } 235 1.3 dholland endfsent(); 236 1.3 dholland __quota_oldfiles_fstab_loaded = 1; 237 1.3 dholland 238 1.3 dholland return; 239 1.3 dholland failed: 240 1.3 dholland warn("Failed reading fstab"); 241 1.3 dholland return; 242 1.3 dholland } 243 1.3 dholland 244 1.3 dholland int 245 1.3 dholland __quota_oldfiles_infstab(const char *mountpoint) 246 1.3 dholland { 247 1.3 dholland return __quota_oldfiles_find_fstabentry(mountpoint) != NULL; 248 1.3 dholland } 249 1.3 dholland 250 1.5 dholland static void 251 1.5 dholland __quota_oldfiles_defquotafile(struct quotahandle *qh, int idtype, 252 1.5 dholland char *buf, size_t maxlen) 253 1.5 dholland { 254 1.5 dholland static const char *const names[] = INITQFNAMES; 255 1.5 dholland 256 1.5 dholland (void)snprintf(buf, maxlen, "%s/%s.%s", 257 1.5 dholland qh->qh_mountpoint, 258 1.10 hannken QUOTAFILENAME, names[idtype]); 259 1.5 dholland } 260 1.5 dholland 261 1.5 dholland const char * 262 1.5 dholland __quota_oldfiles_getquotafile(struct quotahandle *qh, int idtype, 263 1.5 dholland char *buf, size_t maxlen) 264 1.5 dholland { 265 1.5 dholland const struct oldfiles_fstabentry *ofe; 266 1.5 dholland const char *file; 267 1.5 dholland 268 1.5 dholland ofe = __quota_oldfiles_find_fstabentry(qh->qh_mountpoint); 269 1.5 dholland if (ofe == NULL) { 270 1.5 dholland errno = ENXIO; 271 1.5 dholland return NULL; 272 1.5 dholland } 273 1.5 dholland 274 1.5 dholland switch (idtype) { 275 1.5 dholland case USRQUOTA: 276 1.5 dholland if (!ofe->ofe_hasuserquota) { 277 1.5 dholland errno = ENXIO; 278 1.5 dholland return NULL; 279 1.5 dholland } 280 1.5 dholland file = ofe->ofe_userquotafile; 281 1.5 dholland break; 282 1.5 dholland case GRPQUOTA: 283 1.5 dholland if (!ofe->ofe_hasgroupquota) { 284 1.5 dholland errno = ENXIO; 285 1.5 dholland return NULL; 286 1.5 dholland } 287 1.5 dholland file = ofe->ofe_groupquotafile; 288 1.5 dholland break; 289 1.5 dholland default: 290 1.5 dholland errno = EINVAL; 291 1.5 dholland return NULL; 292 1.5 dholland } 293 1.5 dholland 294 1.5 dholland if (file == NULL) { 295 1.5 dholland __quota_oldfiles_defquotafile(qh, idtype, buf, maxlen); 296 1.5 dholland file = buf; 297 1.5 dholland } 298 1.5 dholland return file; 299 1.5 dholland } 300 1.5 dholland 301 1.1 dholland static uint64_t 302 1.2 dholland dqblk_getlimit(uint32_t val) 303 1.1 dholland { 304 1.1 dholland if (val == 0) { 305 1.1 dholland return QUOTA_NOLIMIT; 306 1.1 dholland } else { 307 1.1 dholland return val - 1; 308 1.1 dholland } 309 1.1 dholland } 310 1.1 dholland 311 1.2 dholland static uint32_t 312 1.2 dholland dqblk_setlimit(uint64_t val) 313 1.2 dholland { 314 1.2 dholland if (val == QUOTA_NOLIMIT && val >= 0xffffffffUL) { 315 1.2 dholland return 0; 316 1.2 dholland } else { 317 1.2 dholland return (uint32_t)val + 1; 318 1.2 dholland } 319 1.2 dholland } 320 1.2 dholland 321 1.1 dholland static void 322 1.1 dholland dqblk_getblocks(const struct dqblk *dq, struct quotaval *qv) 323 1.1 dholland { 324 1.2 dholland qv->qv_hardlimit = dqblk_getlimit(dq->dqb_bhardlimit); 325 1.2 dholland qv->qv_softlimit = dqblk_getlimit(dq->dqb_bsoftlimit); 326 1.1 dholland qv->qv_usage = dq->dqb_curblocks; 327 1.1 dholland qv->qv_expiretime = dq->dqb_btime; 328 1.1 dholland qv->qv_grace = QUOTA_NOTIME; 329 1.1 dholland } 330 1.1 dholland 331 1.1 dholland static void 332 1.1 dholland dqblk_getfiles(const struct dqblk *dq, struct quotaval *qv) 333 1.1 dholland { 334 1.2 dholland qv->qv_hardlimit = dqblk_getlimit(dq->dqb_ihardlimit); 335 1.2 dholland qv->qv_softlimit = dqblk_getlimit(dq->dqb_isoftlimit); 336 1.1 dholland qv->qv_usage = dq->dqb_curinodes; 337 1.1 dholland qv->qv_expiretime = dq->dqb_itime; 338 1.1 dholland qv->qv_grace = QUOTA_NOTIME; 339 1.1 dholland } 340 1.1 dholland 341 1.2 dholland static void 342 1.2 dholland dqblk_putblocks(const struct quotaval *qv, struct dqblk *dq) 343 1.2 dholland { 344 1.2 dholland dq->dqb_bhardlimit = dqblk_setlimit(qv->qv_hardlimit); 345 1.2 dholland dq->dqb_bsoftlimit = dqblk_setlimit(qv->qv_softlimit); 346 1.2 dholland dq->dqb_curblocks = qv->qv_usage; 347 1.2 dholland dq->dqb_btime = qv->qv_expiretime; 348 1.2 dholland /* ignore qv->qv_grace */ 349 1.2 dholland } 350 1.2 dholland 351 1.2 dholland static void 352 1.2 dholland dqblk_putfiles(const struct quotaval *qv, struct dqblk *dq) 353 1.2 dholland { 354 1.2 dholland dq->dqb_ihardlimit = dqblk_setlimit(qv->qv_hardlimit); 355 1.2 dholland dq->dqb_isoftlimit = dqblk_setlimit(qv->qv_softlimit); 356 1.2 dholland dq->dqb_curinodes = qv->qv_usage; 357 1.2 dholland dq->dqb_itime = qv->qv_expiretime; 358 1.2 dholland /* ignore qv->qv_grace */ 359 1.2 dholland } 360 1.2 dholland 361 1.1 dholland static int 362 1.1 dholland __quota_oldfiles_open(struct quotahandle *qh, const char *path, int *fd_ret) 363 1.1 dholland { 364 1.1 dholland int fd; 365 1.1 dholland 366 1.1 dholland fd = open(path, O_RDWR); 367 1.1 dholland if (fd < 0 && (errno == EACCES || errno == EROFS)) { 368 1.1 dholland fd = open(path, O_RDONLY); 369 1.1 dholland if (fd < 0) { 370 1.1 dholland return -1; 371 1.1 dholland } 372 1.1 dholland } 373 1.1 dholland *fd_ret = fd; 374 1.1 dholland return 0; 375 1.1 dholland } 376 1.1 dholland 377 1.1 dholland int 378 1.1 dholland __quota_oldfiles_initialize(struct quotahandle *qh) 379 1.1 dholland { 380 1.3 dholland const struct oldfiles_fstabentry *ofe; 381 1.1 dholland char path[PATH_MAX]; 382 1.1 dholland const char *userquotafile, *groupquotafile; 383 1.1 dholland 384 1.3 dholland if (qh->qh_oldfilesopen) { 385 1.1 dholland /* already initialized */ 386 1.1 dholland return 0; 387 1.1 dholland } 388 1.1 dholland 389 1.1 dholland /* 390 1.1 dholland * Find the fstab entry. 391 1.1 dholland */ 392 1.3 dholland ofe = __quota_oldfiles_find_fstabentry(qh->qh_mountpoint); 393 1.3 dholland if (ofe == NULL) { 394 1.1 dholland warnx("%s not found in fstab", qh->qh_mountpoint); 395 1.1 dholland errno = ENXIO; 396 1.1 dholland return -1; 397 1.1 dholland } 398 1.1 dholland 399 1.3 dholland if (!ofe->ofe_hasuserquota && !ofe->ofe_hasgroupquota) { 400 1.1 dholland errno = ENXIO; 401 1.1 dholland return -1; 402 1.1 dholland } 403 1.1 dholland 404 1.3 dholland if (ofe->ofe_hasuserquota) { 405 1.3 dholland userquotafile = ofe->ofe_userquotafile; 406 1.1 dholland if (userquotafile == NULL) { 407 1.5 dholland __quota_oldfiles_defquotafile(qh, USRQUOTA, 408 1.5 dholland path, sizeof(path)); 409 1.1 dholland userquotafile = path; 410 1.1 dholland } 411 1.1 dholland if (__quota_oldfiles_open(qh, userquotafile, 412 1.1 dholland &qh->qh_userfile)) { 413 1.1 dholland return -1; 414 1.1 dholland } 415 1.1 dholland } 416 1.3 dholland if (ofe->ofe_hasgroupquota) { 417 1.3 dholland groupquotafile = ofe->ofe_groupquotafile; 418 1.1 dholland if (groupquotafile == NULL) { 419 1.5 dholland __quota_oldfiles_defquotafile(qh, GRPQUOTA, 420 1.5 dholland path, sizeof(path)); 421 1.1 dholland groupquotafile = path; 422 1.1 dholland } 423 1.1 dholland if (__quota_oldfiles_open(qh, groupquotafile, 424 1.1 dholland &qh->qh_groupfile)) { 425 1.1 dholland return -1; 426 1.1 dholland } 427 1.1 dholland } 428 1.1 dholland 429 1.3 dholland qh->qh_oldfilesopen = 1; 430 1.1 dholland 431 1.1 dholland return 0; 432 1.1 dholland } 433 1.1 dholland 434 1.1 dholland const char * 435 1.1 dholland __quota_oldfiles_getimplname(struct quotahandle *qh) 436 1.1 dholland { 437 1.3 dholland return "ufs/ffs quota v1 file access"; 438 1.1 dholland } 439 1.1 dholland 440 1.5 dholland int 441 1.5 dholland __quota_oldfiles_quotaon(struct quotahandle *qh, int idtype) 442 1.5 dholland { 443 1.5 dholland int result; 444 1.5 dholland 445 1.5 dholland /* 446 1.5 dholland * If we have the quota files open, close them. 447 1.5 dholland */ 448 1.5 dholland 449 1.5 dholland if (qh->qh_oldfilesopen) { 450 1.5 dholland if (qh->qh_userfile >= 0) { 451 1.5 dholland close(qh->qh_userfile); 452 1.5 dholland qh->qh_userfile = -1; 453 1.5 dholland } 454 1.5 dholland if (qh->qh_groupfile >= 0) { 455 1.5 dholland close(qh->qh_groupfile); 456 1.5 dholland qh->qh_groupfile = -1; 457 1.5 dholland } 458 1.5 dholland qh->qh_oldfilesopen = 0; 459 1.5 dholland } 460 1.5 dholland 461 1.5 dholland /* 462 1.5 dholland * Go over to the syscall interface. 463 1.5 dholland */ 464 1.5 dholland 465 1.6 dholland result = __quota_kernel_quotaon(qh, idtype); 466 1.5 dholland if (result < 0) { 467 1.5 dholland return -1; 468 1.5 dholland } 469 1.5 dholland 470 1.5 dholland /* 471 1.5 dholland * We succeeded, so all further access should be via the 472 1.5 dholland * kernel. 473 1.5 dholland */ 474 1.5 dholland 475 1.6 dholland qh->qh_mode = QUOTA_MODE_KERNEL; 476 1.5 dholland return 0; 477 1.5 dholland } 478 1.5 dholland 479 1.1 dholland static int 480 1.1 dholland __quota_oldfiles_doget(struct quotahandle *qh, const struct quotakey *qk, 481 1.1 dholland struct quotaval *qv, int *isallzero) 482 1.1 dholland { 483 1.1 dholland int file; 484 1.1 dholland off_t pos; 485 1.1 dholland struct dqblk dq; 486 1.1 dholland ssize_t result; 487 1.1 dholland 488 1.3 dholland if (!qh->qh_oldfilesopen) { 489 1.3 dholland if (__quota_oldfiles_initialize(qh)) { 490 1.3 dholland return -1; 491 1.3 dholland } 492 1.3 dholland } 493 1.3 dholland 494 1.1 dholland switch (qk->qk_idtype) { 495 1.1 dholland case QUOTA_IDTYPE_USER: 496 1.1 dholland file = qh->qh_userfile; 497 1.1 dholland break; 498 1.1 dholland case QUOTA_IDTYPE_GROUP: 499 1.1 dholland file = qh->qh_groupfile; 500 1.1 dholland break; 501 1.1 dholland default: 502 1.1 dholland errno = EINVAL; 503 1.1 dholland return -1; 504 1.1 dholland } 505 1.1 dholland 506 1.1 dholland if (qk->qk_id == QUOTA_DEFAULTID) { 507 1.1 dholland pos = 0; 508 1.1 dholland } else { 509 1.1 dholland pos = qk->qk_id * sizeof(struct dqblk); 510 1.1 dholland } 511 1.1 dholland 512 1.1 dholland result = pread(file, &dq, sizeof(dq), pos); 513 1.1 dholland if (result < 0) { 514 1.1 dholland return -1; 515 1.2 dholland } else if (result == 0) { 516 1.2 dholland /* Past EOF; no quota info on file for this ID */ 517 1.2 dholland errno = ENOENT; 518 1.2 dholland return -1; 519 1.2 dholland } else if ((size_t)result != sizeof(dq)) { 520 1.1 dholland errno = EFTYPE; 521 1.1 dholland return -1; 522 1.1 dholland } 523 1.1 dholland 524 1.1 dholland switch (qk->qk_objtype) { 525 1.1 dholland case QUOTA_OBJTYPE_BLOCKS: 526 1.1 dholland dqblk_getblocks(&dq, qv); 527 1.1 dholland break; 528 1.1 dholland case QUOTA_OBJTYPE_FILES: 529 1.1 dholland dqblk_getfiles(&dq, qv); 530 1.1 dholland break; 531 1.1 dholland default: 532 1.1 dholland errno = EINVAL; 533 1.1 dholland return -1; 534 1.1 dholland } 535 1.1 dholland 536 1.1 dholland if (qk->qk_id == QUOTA_DEFAULTID) { 537 1.1 dholland qv->qv_usage = 0; 538 1.1 dholland qv->qv_grace = qv->qv_expiretime; 539 1.1 dholland qv->qv_expiretime = QUOTA_NOTIME; 540 1.1 dholland } else if (qk->qk_id == 0) { 541 1.1 dholland qv->qv_hardlimit = 0; 542 1.1 dholland qv->qv_softlimit = 0; 543 1.1 dholland qv->qv_expiretime = QUOTA_NOTIME; 544 1.1 dholland qv->qv_grace = QUOTA_NOTIME; 545 1.1 dholland } 546 1.1 dholland 547 1.1 dholland if (isallzero != NULL) { 548 1.1 dholland if (dq.dqb_bhardlimit == 0 && 549 1.1 dholland dq.dqb_bsoftlimit == 0 && 550 1.1 dholland dq.dqb_curblocks == 0 && 551 1.1 dholland dq.dqb_ihardlimit == 0 && 552 1.1 dholland dq.dqb_isoftlimit == 0 && 553 1.1 dholland dq.dqb_curinodes == 0 && 554 1.1 dholland dq.dqb_btime == 0 && 555 1.1 dholland dq.dqb_itime == 0) { 556 1.1 dholland *isallzero = 1; 557 1.1 dholland } else { 558 1.1 dholland *isallzero = 0; 559 1.1 dholland } 560 1.1 dholland } 561 1.1 dholland 562 1.1 dholland return 0; 563 1.1 dholland } 564 1.1 dholland 565 1.2 dholland static int 566 1.2 dholland __quota_oldfiles_doput(struct quotahandle *qh, const struct quotakey *qk, 567 1.2 dholland const struct quotaval *qv) 568 1.2 dholland { 569 1.2 dholland int file; 570 1.2 dholland off_t pos; 571 1.2 dholland struct quotaval qv2; 572 1.2 dholland struct dqblk dq; 573 1.2 dholland ssize_t result; 574 1.2 dholland 575 1.3 dholland if (!qh->qh_oldfilesopen) { 576 1.3 dholland if (__quota_oldfiles_initialize(qh)) { 577 1.3 dholland return -1; 578 1.3 dholland } 579 1.3 dholland } 580 1.3 dholland 581 1.2 dholland switch (qk->qk_idtype) { 582 1.2 dholland case QUOTA_IDTYPE_USER: 583 1.2 dholland file = qh->qh_userfile; 584 1.2 dholland break; 585 1.2 dholland case QUOTA_IDTYPE_GROUP: 586 1.2 dholland file = qh->qh_groupfile; 587 1.2 dholland break; 588 1.2 dholland default: 589 1.2 dholland errno = EINVAL; 590 1.2 dholland return -1; 591 1.2 dholland } 592 1.2 dholland 593 1.2 dholland if (qk->qk_id == QUOTA_DEFAULTID) { 594 1.2 dholland pos = 0; 595 1.2 dholland } else { 596 1.2 dholland pos = qk->qk_id * sizeof(struct dqblk); 597 1.2 dholland } 598 1.2 dholland 599 1.2 dholland result = pread(file, &dq, sizeof(dq), pos); 600 1.2 dholland if (result < 0) { 601 1.2 dholland return -1; 602 1.2 dholland } else if (result == 0) { 603 1.2 dholland /* Past EOF; fill in a blank dq to start from */ 604 1.2 dholland dq.dqb_bhardlimit = 0; 605 1.2 dholland dq.dqb_bsoftlimit = 0; 606 1.2 dholland dq.dqb_curblocks = 0; 607 1.2 dholland dq.dqb_ihardlimit = 0; 608 1.2 dholland dq.dqb_isoftlimit = 0; 609 1.2 dholland dq.dqb_curinodes = 0; 610 1.2 dholland dq.dqb_btime = 0; 611 1.2 dholland dq.dqb_itime = 0; 612 1.2 dholland } else if ((size_t)result != sizeof(dq)) { 613 1.2 dholland errno = EFTYPE; 614 1.2 dholland return -1; 615 1.2 dholland } 616 1.2 dholland 617 1.2 dholland switch (qk->qk_objtype) { 618 1.2 dholland case QUOTA_OBJTYPE_BLOCKS: 619 1.2 dholland dqblk_getblocks(&dq, &qv2); 620 1.2 dholland break; 621 1.2 dholland case QUOTA_OBJTYPE_FILES: 622 1.2 dholland dqblk_getfiles(&dq, &qv2); 623 1.2 dholland break; 624 1.2 dholland default: 625 1.2 dholland errno = EINVAL; 626 1.2 dholland return -1; 627 1.2 dholland } 628 1.2 dholland 629 1.2 dholland if (qk->qk_id == QUOTA_DEFAULTID) { 630 1.2 dholland qv2.qv_hardlimit = qv->qv_hardlimit; 631 1.2 dholland qv2.qv_softlimit = qv->qv_softlimit; 632 1.2 dholland /* leave qv2.qv_usage unchanged */ 633 1.2 dholland qv2.qv_expiretime = qv->qv_grace; 634 1.2 dholland /* skip qv2.qv_grace */ 635 1.2 dholland 636 1.2 dholland /* ignore qv->qv_usage */ 637 1.2 dholland /* ignore qv->qv_expiretime */ 638 1.2 dholland } else if (qk->qk_id == 0) { 639 1.2 dholland /* leave qv2.qv_hardlimit unchanged */ 640 1.2 dholland /* leave qv2.qv_softlimit unchanged */ 641 1.2 dholland qv2.qv_usage = qv->qv_usage; 642 1.2 dholland /* leave qv2.qv_expiretime unchanged */ 643 1.2 dholland /* skip qv2.qv_grace */ 644 1.2 dholland 645 1.2 dholland /* ignore qv->qv_hardlimit */ 646 1.2 dholland /* ignore qv->qv_softlimit */ 647 1.2 dholland /* ignore qv->qv_expiretime */ 648 1.2 dholland /* ignore qv->qv_grace */ 649 1.2 dholland } else { 650 1.2 dholland qv2 = *qv; 651 1.2 dholland } 652 1.2 dholland 653 1.2 dholland switch (qk->qk_objtype) { 654 1.2 dholland case QUOTA_OBJTYPE_BLOCKS: 655 1.2 dholland dqblk_putblocks(&qv2, &dq); 656 1.2 dholland break; 657 1.2 dholland case QUOTA_OBJTYPE_FILES: 658 1.2 dholland dqblk_putfiles(&qv2, &dq); 659 1.2 dholland break; 660 1.2 dholland default: 661 1.2 dholland errno = EINVAL; 662 1.2 dholland return -1; 663 1.2 dholland } 664 1.2 dholland 665 1.2 dholland result = pwrite(file, &dq, sizeof(dq), pos); 666 1.2 dholland if (result < 0) { 667 1.2 dholland return -1; 668 1.2 dholland } else if ((size_t)result != sizeof(dq)) { 669 1.2 dholland /* ? */ 670 1.2 dholland errno = EFTYPE; 671 1.2 dholland return -1; 672 1.2 dholland } 673 1.2 dholland 674 1.2 dholland return 0; 675 1.2 dholland } 676 1.2 dholland 677 1.1 dholland int 678 1.1 dholland __quota_oldfiles_get(struct quotahandle *qh, const struct quotakey *qk, 679 1.1 dholland struct quotaval *qv) 680 1.1 dholland { 681 1.1 dholland return __quota_oldfiles_doget(qh, qk, qv, NULL); 682 1.1 dholland } 683 1.1 dholland 684 1.2 dholland int 685 1.2 dholland __quota_oldfiles_put(struct quotahandle *qh, const struct quotakey *qk, 686 1.2 dholland const struct quotaval *qv) 687 1.2 dholland { 688 1.2 dholland return __quota_oldfiles_doput(qh, qk, qv); 689 1.2 dholland } 690 1.2 dholland 691 1.2 dholland int 692 1.2 dholland __quota_oldfiles_delete(struct quotahandle *qh, const struct quotakey *qk) 693 1.2 dholland { 694 1.2 dholland struct quotaval qv; 695 1.2 dholland 696 1.2 dholland quotaval_clear(&qv); 697 1.2 dholland return __quota_oldfiles_doput(qh, qk, &qv); 698 1.2 dholland } 699 1.2 dholland 700 1.1 dholland struct oldfiles_quotacursor * 701 1.1 dholland __quota_oldfiles_cursor_create(struct quotahandle *qh) 702 1.1 dholland { 703 1.1 dholland struct oldfiles_quotacursor *oqc; 704 1.1 dholland struct stat st; 705 1.1 dholland int serrno; 706 1.1 dholland 707 1.3 dholland /* quota_opencursor calls initialize for us, no need to do it here */ 708 1.3 dholland 709 1.1 dholland oqc = malloc(sizeof(*oqc)); 710 1.1 dholland if (oqc == NULL) { 711 1.1 dholland return NULL; 712 1.1 dholland } 713 1.1 dholland 714 1.1 dholland oqc->oqc_didusers = 0; 715 1.1 dholland oqc->oqc_didgroups = 0; 716 1.1 dholland oqc->oqc_diddefault = 0; 717 1.1 dholland oqc->oqc_pos = 0; 718 1.1 dholland oqc->oqc_didblocks = 0; 719 1.1 dholland 720 1.1 dholland if (qh->qh_userfile >= 0) { 721 1.1 dholland oqc->oqc_doingusers = 1; 722 1.1 dholland } else { 723 1.1 dholland oqc->oqc_doingusers = 0; 724 1.1 dholland oqc->oqc_didusers = 1; 725 1.1 dholland } 726 1.1 dholland 727 1.1 dholland if (qh->qh_groupfile >= 0) { 728 1.1 dholland oqc->oqc_doinggroups = 1; 729 1.1 dholland } else { 730 1.1 dholland oqc->oqc_doinggroups = 0; 731 1.1 dholland oqc->oqc_didgroups = 1; 732 1.1 dholland } 733 1.1 dholland 734 1.1 dholland if (fstat(qh->qh_userfile, &st) < 0) { 735 1.1 dholland serrno = errno; 736 1.1 dholland free(oqc); 737 1.1 dholland errno = serrno; 738 1.1 dholland return NULL; 739 1.1 dholland } 740 1.1 dholland oqc->oqc_numusers = st.st_size / sizeof(struct dqblk); 741 1.1 dholland 742 1.1 dholland if (fstat(qh->qh_groupfile, &st) < 0) { 743 1.1 dholland serrno = errno; 744 1.1 dholland free(oqc); 745 1.1 dholland errno = serrno; 746 1.1 dholland return NULL; 747 1.1 dholland } 748 1.1 dholland oqc->oqc_numgroups = st.st_size / sizeof(struct dqblk); 749 1.1 dholland 750 1.1 dholland return oqc; 751 1.1 dholland } 752 1.1 dholland 753 1.1 dholland void 754 1.1 dholland __quota_oldfiles_cursor_destroy(struct oldfiles_quotacursor *oqc) 755 1.1 dholland { 756 1.1 dholland free(oqc); 757 1.1 dholland } 758 1.1 dholland 759 1.1 dholland int 760 1.1 dholland __quota_oldfiles_cursor_skipidtype(struct oldfiles_quotacursor *oqc, 761 1.7 dholland int idtype) 762 1.1 dholland { 763 1.1 dholland switch (idtype) { 764 1.1 dholland case QUOTA_IDTYPE_USER: 765 1.1 dholland oqc->oqc_doingusers = 0; 766 1.1 dholland oqc->oqc_didusers = 1; 767 1.1 dholland break; 768 1.1 dholland case QUOTA_IDTYPE_GROUP: 769 1.1 dholland oqc->oqc_doinggroups = 0; 770 1.1 dholland oqc->oqc_didgroups = 1; 771 1.1 dholland break; 772 1.1 dholland default: 773 1.1 dholland errno = EINVAL; 774 1.1 dholland return -1; 775 1.1 dholland } 776 1.1 dholland return 0; 777 1.1 dholland } 778 1.1 dholland 779 1.1 dholland int 780 1.1 dholland __quota_oldfiles_cursor_get(struct quotahandle *qh, 781 1.1 dholland struct oldfiles_quotacursor *oqc, 782 1.1 dholland struct quotakey *key, struct quotaval *val) 783 1.1 dholland { 784 1.1 dholland unsigned maxpos; 785 1.1 dholland int isallzero; 786 1.1 dholland 787 1.1 dholland /* in case one of the sizes is zero */ 788 1.1 dholland if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) { 789 1.1 dholland oqc->oqc_didusers = 1; 790 1.1 dholland } 791 1.1 dholland if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) { 792 1.1 dholland oqc->oqc_didgroups = 1; 793 1.1 dholland } 794 1.1 dholland 795 1.1 dholland again: 796 1.1 dholland /* 797 1.1 dholland * Figure out what to get 798 1.1 dholland */ 799 1.1 dholland 800 1.1 dholland if (!oqc->oqc_didusers) { 801 1.1 dholland key->qk_idtype = QUOTA_IDTYPE_USER; 802 1.1 dholland maxpos = oqc->oqc_numusers; 803 1.1 dholland } else if (!oqc->oqc_didgroups) { 804 1.1 dholland key->qk_idtype = QUOTA_IDTYPE_GROUP; 805 1.1 dholland maxpos = oqc->oqc_numgroups; 806 1.1 dholland } else { 807 1.1 dholland errno = ENOENT; 808 1.1 dholland return -1; 809 1.1 dholland } 810 1.1 dholland 811 1.1 dholland if (!oqc->oqc_diddefault) { 812 1.1 dholland key->qk_id = QUOTA_DEFAULTID; 813 1.1 dholland } else { 814 1.1 dholland key->qk_id = oqc->oqc_pos; 815 1.1 dholland } 816 1.1 dholland 817 1.1 dholland if (!oqc->oqc_didblocks) { 818 1.1 dholland key->qk_objtype = QUOTA_OBJTYPE_BLOCKS; 819 1.1 dholland } else { 820 1.1 dholland key->qk_objtype = QUOTA_OBJTYPE_FILES; 821 1.1 dholland } 822 1.1 dholland 823 1.1 dholland /* 824 1.1 dholland * Get it 825 1.1 dholland */ 826 1.1 dholland 827 1.1 dholland if (__quota_oldfiles_doget(qh, key, val, &isallzero)) { 828 1.1 dholland return -1; 829 1.1 dholland } 830 1.1 dholland 831 1.1 dholland /* 832 1.1 dholland * Advance the cursor 833 1.1 dholland */ 834 1.1 dholland if (!oqc->oqc_didblocks) { 835 1.1 dholland oqc->oqc_didblocks = 1; 836 1.1 dholland } else { 837 1.1 dholland oqc->oqc_didblocks = 0; 838 1.1 dholland if (!oqc->oqc_diddefault) { 839 1.1 dholland oqc->oqc_diddefault = 1; 840 1.1 dholland } else { 841 1.1 dholland oqc->oqc_pos++; 842 1.1 dholland if (oqc->oqc_pos >= maxpos) { 843 1.1 dholland oqc->oqc_pos = 0; 844 1.1 dholland oqc->oqc_diddefault = 0; 845 1.1 dholland if (!oqc->oqc_didusers) { 846 1.1 dholland oqc->oqc_didusers = 1; 847 1.1 dholland } else { 848 1.1 dholland oqc->oqc_didgroups = 1; 849 1.1 dholland } 850 1.1 dholland } 851 1.1 dholland } 852 1.1 dholland } 853 1.1 dholland 854 1.1 dholland /* 855 1.1 dholland * If we got an all-zero dqblk (e.g. from the middle of a hole 856 1.1 dholland * in the quota file) don't bother returning it to the caller. 857 1.1 dholland * 858 1.1 dholland * ...unless we're at the end of the data, to avoid going past 859 1.1 dholland * the end and generating a spurious failure. There's no 860 1.1 dholland * reasonable way to make _atend detect empty entries at the 861 1.1 dholland * end of the quota files. 862 1.1 dholland */ 863 1.1 dholland if (isallzero && (!oqc->oqc_didusers || !oqc->oqc_didgroups)) { 864 1.1 dholland goto again; 865 1.1 dholland } 866 1.1 dholland return 0; 867 1.1 dholland } 868 1.1 dholland 869 1.1 dholland int 870 1.1 dholland __quota_oldfiles_cursor_getn(struct quotahandle *qh, 871 1.1 dholland struct oldfiles_quotacursor *oqc, 872 1.1 dholland struct quotakey *keys, struct quotaval *vals, 873 1.1 dholland unsigned maxnum) 874 1.1 dholland { 875 1.1 dholland unsigned i; 876 1.1 dholland 877 1.1 dholland if (maxnum > INT_MAX) { 878 1.1 dholland /* joker, eh? */ 879 1.1 dholland errno = EINVAL; 880 1.1 dholland return -1; 881 1.1 dholland } 882 1.1 dholland 883 1.1 dholland for (i=0; i<maxnum; i++) { 884 1.1 dholland if (__quota_oldfiles_cursor_atend(oqc)) { 885 1.1 dholland break; 886 1.1 dholland } 887 1.1 dholland if (__quota_oldfiles_cursor_get(qh, oqc, &keys[i], &vals[i])) { 888 1.1 dholland if (i > 0) { 889 1.1 dholland /* 890 1.1 dholland * Succeed witih what we have so far; 891 1.1 dholland * the next attempt will hit the same 892 1.1 dholland * error again. 893 1.1 dholland */ 894 1.1 dholland break; 895 1.1 dholland } 896 1.1 dholland return -1; 897 1.1 dholland } 898 1.1 dholland } 899 1.1 dholland return i; 900 1.1 dholland 901 1.1 dholland } 902 1.1 dholland 903 1.1 dholland int 904 1.1 dholland __quota_oldfiles_cursor_atend(struct oldfiles_quotacursor *oqc) 905 1.1 dholland { 906 1.1 dholland /* in case one of the sizes is zero */ 907 1.1 dholland if (!oqc->oqc_didusers && oqc->oqc_pos >= oqc->oqc_numusers) { 908 1.1 dholland oqc->oqc_didusers = 1; 909 1.1 dholland } 910 1.1 dholland if (!oqc->oqc_didgroups && oqc->oqc_pos >= oqc->oqc_numgroups) { 911 1.1 dholland oqc->oqc_didgroups = 1; 912 1.1 dholland } 913 1.1 dholland 914 1.1 dholland return oqc->oqc_didusers && oqc->oqc_didgroups; 915 1.1 dholland } 916 1.1 dholland 917 1.1 dholland int 918 1.1 dholland __quota_oldfiles_cursor_rewind(struct oldfiles_quotacursor *oqc) 919 1.1 dholland { 920 1.1 dholland oqc->oqc_didusers = 0; 921 1.1 dholland oqc->oqc_didgroups = 0; 922 1.1 dholland oqc->oqc_diddefault = 0; 923 1.1 dholland oqc->oqc_pos = 0; 924 1.1 dholland oqc->oqc_didblocks = 0; 925 1.1 dholland return 0; 926 1.1 dholland } 927