/* * Q_GETQUOTA - return current values in a dqblk structure. */ static int getquota(uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, cred_t *cr) { struct dquot *dqp; struct dquot *xdqp; struct dqblk dqb; int error = 0; if (uid != crgetruid(cr) && secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (ESRCH); } error = getdiskquota(uid, ufsvfsp, 0, &xdqp); if (error) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); } dqp = xdqp; mutex_enter(&dqp->dq_lock); if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) { error = ESRCH; } else { bcopy(&dqp->dq_dqb, &dqb, sizeof (struct dqblk)); } dqput(dqp); mutex_exit(&dqp->dq_lock); rw_exit(&ufsvfsp->vfs_dqrwlock); if (error == 0 && copyout(&dqb, addr, sizeof (struct dqblk)) != 0) error = EFAULT; return (error); }
static void showquotas(uid_t uid, char *name) { struct mnttab mnt; FILE *mtab; struct dqblk dqblk; uid_t myuid; struct failed_srv { char *serv_name; struct failed_srv *next; }; struct failed_srv *failed_srv_list = NULL; int rc; char my_zonename[ZONENAME_MAX]; zoneid_t my_zoneid = getzoneid(); myuid = getuid(); if (uid != myuid && myuid != 0) { printf("quota: %s (uid %d): permission denied\n", name, uid); zexit(32); } memset(my_zonename, '\0', ZONENAME_MAX); getzonenamebyid(my_zoneid, my_zonename, ZONENAME_MAX); if (vflag) heading(uid, name); mtab = fopen(MNTTAB, "r"); while (getmntent(mtab, &mnt) == NULL) { if (strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) == 0) { bzero(&dqblk, sizeof (dqblk)); if (getzfsquota(name, mnt.mnt_special, &dqblk)) continue; } else if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) { if (nolocalquota || (quotactl(Q_GETQUOTA, mnt.mnt_mountp, uid, &dqblk) != 0 && !(vflag && getdiskquota(&mnt, uid, &dqblk)))) continue; } else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) { struct replica *rl; int count; char *mntopt = NULL; /* * Skip checking quotas for file systems mounted * in other zones. Zone names will be passed in * following format from hasmntopt(): * "zone=<zone-name>,<mnt options...>" */ if ((mntopt = hasmntopt(&mnt, MNTOPT_ZONE)) && (my_zonename[0] != '\0')) { mntopt += strcspn(mntopt, "=") + 1; if (strncmp(mntopt, my_zonename, strcspn(mntopt, ",")) != 0) continue; } if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts)) continue; /* * Skip quota processing if mounted with public * option. We are not likely to be able to pierce * a fire wall to contact the quota server. */ if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts)) continue; rl = parse_replica(mnt.mnt_special, &count); if (rl == NULL) { if (count < 0) fprintf(stderr, "cannot find hostname " "and/or pathname for %s\n", mnt.mnt_mountp); else fprintf(stderr, "no memory to parse " "mnttab entry for %s\n", mnt.mnt_mountp); continue; } /* * We skip quota reporting on mounts with replicas * for the following reasons: * * (1) Very little point in reporting quotas on * a set of read-only replicas ... how will the * user correct the problem? * * (2) Which replica would we report the quota * for? If we pick the current replica, what * happens when a fail over event occurs? The * next time quota is run, the quota will look * all different, or there won't even be one. * This has the potential to break scripts. * * If we prnt quouta for all replicas, how do * we present the output without breaking scripts? */ if (count > 1) { free_replica(rl, count); continue; } /* * Skip file systems mounted using public fh. * We are not likely to be able to pierce * a fire wall to contact the quota server. */ if (strcmp(rl[0].host, "nfs") == 0 && strncmp(rl[0].path, "//", 2) == 0) { free_replica(rl, count); continue; } /* * Skip getting quotas from failing servers */ if (failed_srv_list != NULL) { struct failed_srv *tmp_list; int found_failed = 0; size_t len = strlen(rl[0].host); tmp_list = failed_srv_list; do { if (strncasecmp(rl[0].host, tmp_list->serv_name, len) == 0) { found_failed = 1; break; } } while ((tmp_list = tmp_list->next) != NULL); if (found_failed) { free_replica(rl, count); continue; } } rc = getnfsquota(rl[0].host, rl[0].path, uid, &dqblk); if (rc != RPC_SUCCESS) { size_t len; struct failed_srv *tmp_srv; /* * Failed to get quota from this server. Add * this server to failed_srv_list and skip * getting quotas for other mounted filesystems * from this server. */ if (rc == RPC_TIMEDOUT || rc == RPC_CANTSEND) { len = strlen(rl[0].host); tmp_srv = (struct failed_srv *)malloc( sizeof (struct failed_srv)); tmp_srv->serv_name = (char *)malloc( len * sizeof (char) + 1); strncpy(tmp_srv->serv_name, rl[0].host, len); tmp_srv->serv_name[len] = '\0'; tmp_srv->next = failed_srv_list; failed_srv_list = tmp_srv; } free_replica(rl, count); continue; } free_replica(rl, count); } else { continue; } if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0 && dqblk.dqb_fsoftlimit == 0 && dqblk.dqb_fhardlimit == 0) continue; if (vflag) prquota(&mnt, &dqblk); else warn(&mnt, &dqblk); } /* * Free list of failed servers */ while (failed_srv_list != NULL) { struct failed_srv *tmp_srv = failed_srv_list; failed_srv_list = failed_srv_list->next; free(tmp_srv->serv_name); free(tmp_srv); } fclose(mtab); }
/* * Set the quota file up for a particular file system. * Called as the result of a quotaon (Q_QUOTAON) ioctl. */ static int opendq( struct ufsvfs *ufsvfsp, struct vnode *vp, /* quota file */ struct cred *cr) { struct inode *qip; struct dquot *dqp; int error; int quotaon = 0; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); VN_HOLD(vp); /* * Check to be sure its a regular file. */ if (vp->v_type != VREG) { VN_RELE(vp); return (EACCES); } rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * We have vfs_dqrwlock as writer, so if quotas are disabled, * then vfs_qinod should be NULL or we have a race somewhere. */ ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0)); if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) { /* * Quotas are already enabled on this file system. * * If the "quotas" file was replaced (different inode) * while quotas were enabled we don't want to re-enable * them with a new "quotas" file. Simply print a warning * message to the console, release the new vnode, and * return. * XXX - The right way to fix this is to return EBUSY * for the ioctl() issued by 'quotaon'. */ if (VTOI(vp) != ufsvfsp->vfs_qinod) { cmn_err(CE_WARN, "Previous quota file still in use." " Disable quotas on %s before enabling.\n", VTOI(vp)->i_fs->fs_fsmnt); VN_RELE(vp); rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } (void) quotasync(ufsvfsp, /* do_lock */ 0); /* remove extra hold on quota file */ VN_RELE(vp); quotaon++; qip = ufsvfsp->vfs_qinod; } else { int qlen; ufsvfsp->vfs_qinod = VTOI(vp); qip = ufsvfsp->vfs_qinod; /* * Force the file to have no partially allocated blocks * to prevent a realloc from changing the location of * the data. We must do this even if not logging in * case we later remount to logging. */ qlen = qip->i_fs->fs_bsize * NDADDR; /* * Largefiles: i_size needs to be atomically accessed now. */ rw_enter(&qip->i_contents, RW_WRITER); if (qip->i_size < qlen) { if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0) cmn_err(CE_WARN, "opendq failed to remove frags" " from quota file\n"); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size, B_INVAL, kcred, NULL); } else { rw_exit(&qip->i_contents); } TRANS_MATA_IGET(ufsvfsp, qip); } /* * The file system time limits are in the dquot for uid 0. * The time limits set the relative time the other users * can be over quota for this file system. * If it is zero a default is used (see quota.h). */ error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp); if (error == 0) { mutex_enter(&dqp->dq_lock); ufsvfsp->vfs_btimelimit = (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT); ufsvfsp->vfs_ftimelimit = (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT); ufsvfsp->vfs_qflags = MQ_ENABLED; /* enable quotas */ vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0); dqput(dqp); mutex_exit(&dqp->dq_lock); } else if (!quotaon) { /* * Some sort of I/O error on the quota file, and quotas were * not already on when we got here so clean up. */ ufsvfsp->vfs_qflags = 0; ufsvfsp->vfs_qinod = NULL; VN_RELE(ITOV(qip)); } /* * If quotas are enabled update all valid inodes in the * cache with quota information. */ if (ufsvfsp->vfs_qflags & MQ_ENABLED) { (void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp); } rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); }
/* * Set various fields of the dqblk according to the command. * Q_SETQUOTA - assign an entire dqblk structure. * Q_SETQLIM - assign a dqblk structure except for the usage. */ static int setquota(int cmd, uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, struct cred *cr) { struct dquot *dqp; struct inode *qip; struct dquot *xdqp; struct dqblk newlim; int error; int scan_type = SQD_TYPE_NONE; daddr_t bn; int contig; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * Quotas are not enabled on this file system so there is * nothing more to do. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (ESRCH); } /* * At this point, the quota subsystem is quiescent on this file * system so we can do all the work necessary to modify the quota * information for this user. */ if (copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)) != 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (EFAULT); } error = getdiskquota(uid, ufsvfsp, 0, &xdqp); if (error) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); } dqp = xdqp; /* * Don't change disk usage on Q_SETQLIM */ mutex_enter(&dqp->dq_lock); if (cmd == Q_SETQLIM) { newlim.dqb_curblocks = dqp->dq_curblocks; newlim.dqb_curfiles = dqp->dq_curfiles; } if (uid == 0) { /* * Timelimits for uid 0 set the relative time * the other users can be over quota for this file system. * If it is zero a default is used (see quota.h). */ ufsvfsp->vfs_btimelimit = newlim.dqb_btimelimit? newlim.dqb_btimelimit: DQ_BTIMELIMIT; ufsvfsp->vfs_ftimelimit = newlim.dqb_ftimelimit? newlim.dqb_ftimelimit: DQ_FTIMELIMIT; } else { if (newlim.dqb_bsoftlimit && newlim.dqb_curblocks >= newlim.dqb_bsoftlimit) { if (dqp->dq_bsoftlimit == 0 || dqp->dq_curblocks < dqp->dq_bsoftlimit) { /* If we're suddenly over the limit(s), */ /* start the timer(s) */ newlim.dqb_btimelimit = (uint32_t)gethrestime_sec() + ufsvfsp->vfs_btimelimit; dqp->dq_flags &= ~DQ_BLKS; } else { /* If we're currently over the soft */ /* limit and were previously over the */ /* soft limit then preserve the old */ /* time limit but make sure the DQ_BLKS */ /* flag is set since we must have been */ /* previously warned. */ newlim.dqb_btimelimit = dqp->dq_btimelimit; dqp->dq_flags |= DQ_BLKS; } } else { /* Either no quota or under quota, clear time limit */ newlim.dqb_btimelimit = 0; dqp->dq_flags &= ~DQ_BLKS; } if (newlim.dqb_fsoftlimit && newlim.dqb_curfiles >= newlim.dqb_fsoftlimit) { if (dqp->dq_fsoftlimit == 0 || dqp->dq_curfiles < dqp->dq_fsoftlimit) { /* If we're suddenly over the limit(s), */ /* start the timer(s) */ newlim.dqb_ftimelimit = (uint32_t)gethrestime_sec() + ufsvfsp->vfs_ftimelimit; dqp->dq_flags &= ~DQ_FILES; } else { /* If we're currently over the soft */ /* limit and were previously over the */ /* soft limit then preserve the old */ /* time limit but make sure the */ /* DQ_FILES flag is set since we must */ /* have been previously warned. */ newlim.dqb_ftimelimit = dqp->dq_ftimelimit; dqp->dq_flags |= DQ_FILES; } } else { /* Either no quota or under quota, clear time limit */ newlim.dqb_ftimelimit = 0; dqp->dq_flags &= ~DQ_FILES; } } /* * If there was previously no limit and there is now at least * one limit, then any inodes in the cache have NULL d_iquot * fields (getinoquota() returns NULL when there are no limits). */ if ((dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) && (newlim.dqb_fhardlimit || newlim.dqb_fsoftlimit || newlim.dqb_bhardlimit || newlim.dqb_bsoftlimit)) { scan_type = SQD_TYPE_LIMIT; } /* * If there was previously at least one limit and there is now * no limit, then any inodes in the cache have non-NULL d_iquot * fields need to be reset to NULL. */ else if ((dqp->dq_fhardlimit || dqp->dq_fsoftlimit || dqp->dq_bhardlimit || dqp->dq_bsoftlimit) && (newlim.dqb_fhardlimit == 0 && newlim.dqb_fsoftlimit == 0 && newlim.dqb_bhardlimit == 0 && newlim.dqb_bsoftlimit == 0)) { scan_type = SQD_TYPE_NO_LIMIT; } dqp->dq_dqb = newlim; dqp->dq_flags |= DQ_MOD; /* * push the new quota to disk now. If this is a trans device * then force the page out with ufs_putpage so it will be deltaed * by ufs_startio. */ qip = ufsvfsp->vfs_qinod; rw_enter(&qip->i_contents, RW_WRITER); (void) ufs_rdwri(UIO_WRITE, FWRITE | FSYNC, qip, (caddr_t)&dqp->dq_dqb, sizeof (struct dqblk), dqoff(uid), UIO_SYSSPACE, (int *)NULL, kcred); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(ITOV(qip), dqoff(dqp->dq_uid) & ~qip->i_fs->fs_bmask, qip->i_fs->fs_bsize, B_INVAL, kcred, NULL); /* * We must set the dq_mof even if not we are not logging in case * we are later remount to logging. */ contig = 0; rw_enter(&qip->i_contents, RW_WRITER); error = bmap_read(qip, dqoff(dqp->dq_uid), &bn, &contig); rw_exit(&qip->i_contents); if (error || (bn == UFS_HOLE)) { dqp->dq_mof = UFS_HOLE; } else { dqp->dq_mof = ldbtob(bn) + (offset_t)((dqoff(dqp->dq_uid)) & (DEV_BSIZE - 1)); } dqp->dq_flags &= ~DQ_MOD; dqput(dqp); mutex_exit(&dqp->dq_lock); if (scan_type) { struct setquota_data sqd; sqd.sqd_type = scan_type; sqd.sqd_ufsvfsp = ufsvfsp; sqd.sqd_uid = uid; (void) ufs_scan_inodes(0, setquota_scan_inode, &sqd, ufsvfsp); } rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); }
static void showquotas(uid_t uid, char *name) { struct mnttab mnt; FILE *mtab; struct dqblk dqblk; uid_t myuid; myuid = getuid(); if (uid != myuid && myuid != 0) { printf("quota: %s (uid %d): permission denied\n", name, uid); exit(32); } if (vflag) heading(uid, name); mtab = fopen(MNTTAB, "r"); while (getmntent(mtab, &mnt) == NULL) { if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) { if (nolocalquota || (quotactl(Q_GETQUOTA, mnt.mnt_mountp, uid, &dqblk) != 0 && !(vflag && getdiskquota(&mnt, uid, &dqblk)))) continue; } else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) { struct replica *rl; int count; if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts)) continue; /* * Skip quota processing if mounted with public * option. We are not likely to be able to pierce * a fire wall to contact the quota server. */ if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts)) continue; rl = parse_replica(mnt.mnt_special, &count); if (rl == NULL) { if (count < 0) fprintf(stderr, "cannot find hostname " "and/or pathname for %s\n", mnt.mnt_mountp); else fprintf(stderr, "no memory to parse " "mnttab entry for %s\n", mnt.mnt_mountp); continue; } /* * We skip quota reporting on mounts with replicas * for the following reasons: * * (1) Very little point in reporting quotas on * a set of read-only replicas ... how will the * user correct the problem? * * (2) Which replica would we report the quota * for? If we pick the current replica, what * happens when a fail over event occurs? The * next time quota is run, the quota will look * all different, or there won't even be one. * This has the potential to break scripts. * * If we prnt quouta for all replicas, how do * we present the output without breaking scripts? */ if (count > 1) { free_replica(rl, count); continue; } /* * Skip file systems mounted using public fh. * We are not likely to be able to pierce * a fire wall to contact the quota server. */ if (strcmp(rl[0].host, "nfs") == 0 && strncmp(rl[0].path, "//", 2) == 0) { free_replica(rl, count); continue; } if (!getnfsquota(rl[0].host, rl[0].path, uid, &dqblk)) { free_replica(rl, count); continue; } free_replica(rl, count); } else { continue; } if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0 && dqblk.dqb_fsoftlimit == 0 && dqblk.dqb_fhardlimit == 0) continue; if (vflag) prquota(&mnt, &dqblk); else warn(&mnt, &dqblk); } fclose(mtab); }