+ mnt = argv[optind];
+
+ rc = llapi_quotactl(mnt, &qctl);
+ if (rc) {
+ if (*obd_type)
+ fprintf(stderr, "%s %s ", obd_type, obd_uuid);
+ fprintf(stderr, "quotaoff failed: %s\n", strerror(errno));
+ return rc;
+ }
+
+ return 0;
+}
+
+static int name2id(unsigned int *id, char *name, int type)
+{
+ if (type == USRQUOTA) {
+ struct passwd *entry;
+
+ if (!(entry = getpwnam(name))) {
+ if (!errno)
+ errno = ENOENT;
+ return -1;
+ }
+
+ *id = entry->pw_uid;
+ } else {
+ struct group *entry;
+
+ if (!(entry = getgrnam(name))) {
+ if (!errno)
+ errno = ENOENT;
+ return -1;
+ }
+
+ *id = entry->gr_gid;
+ }
+
+ return 0;
+}
+
+static int id2name(char **name, unsigned int id, int type)
+{
+ if (type == USRQUOTA) {
+ struct passwd *entry;
+
+ if (!(entry = getpwuid(id))) {
+ if (!errno)
+ errno = ENOENT;
+ return -1;
+ }
+
+ *name = entry->pw_name;
+ } else {
+ struct group *entry;
+
+ if (!(entry = getgrgid(id))) {
+ if (!errno)
+ errno = ENOENT;
+ return -1;
+ }
+
+ *name = entry->gr_name;
+ }
+
+ return 0;
+}
+
+#define ARG2INT(nr, str, msg) \
+do { \
+ char *endp; \
+ nr = strtol(str, &endp, 0); \
+ if (*endp) { \
+ fprintf(stderr, "error: bad %s: %s\n", msg, str); \
+ return CMD_HELP; \
+ } \
+} while (0)
+
+#define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
+
+/* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
+ * returns the value or ULONG_MAX on integer overflow or incorrect format
+ * Notes:
+ * 1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
+ * 2. specifiers may be encountered multiple times (2s3s is 5 seconds)
+ * 3. empty integer value is interpreted as 0
+ */
+
+static unsigned long str2sec(const char* timestr) {
+ const char spec[] = "smhdw";
+ const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
+ unsigned long val = 0;
+ char *tail;
+
+ if (strpbrk(timestr, spec) == NULL) {
+ /* no specifiers inside the time string,
+ should treat it as an integer value */
+ val = strtoul(timestr, &tail, 10);
+ return *tail ? ULONG_MAX : val;
+ }
+
+ /* format string is XXwXXdXXhXXmXXs */
+ while (*timestr) {
+ unsigned long v;
+ int ind;
+ char* ptr;
+
+ v = strtoul(timestr, &tail, 10);
+ if (v == ULONG_MAX || *tail == '\0')
+ /* value too large (ULONG_MAX or more)
+ or missing specifier */
+ goto error;
+
+ ptr = strchr(spec, *tail);
+ if (ptr == NULL)
+ /* unknown specifier */
+ goto error;
+
+ ind = ptr - spec;
+
+ /* check if product will overflow the type */
+ if (!(v < ULONG_MAX / mult[ind]))
+ goto error;
+
+ ADD_OVERFLOW(val, mult[ind] * v);
+ if (val == ULONG_MAX)
+ goto error;
+
+ timestr = tail + 1;
+ }
+
+ return val;
+
+error:
+ return ULONG_MAX;
+}
+
+int lfs_setquota(int argc, char **argv)
+{
+ int c;
+ char *mnt;
+ struct if_quotactl qctl;
+ char *obd_type = qctl.obd_type;
+ char *obd_uuid = qctl.obd_uuid.uuid;
+ int rc;
+
+ memset(&qctl, 0, sizeof(qctl));
+ qctl.qc_cmd = LUSTRE_Q_SETQUOTA;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "ugt")) != -1) {
+ switch (c) {
+ case 'u':
+ qctl.qc_type |= 0x01;
+ break;
+ case 'g':
+ qctl.qc_type |= 0x02;
+ break;
+ case 't':
+ qctl.qc_cmd = LUSTRE_Q_SETINFO;
+ break;
+ default:
+ fprintf(stderr, "error: %s: option '-%c' "
+ "unrecognized\n", argv[0], c);
+ return CMD_HELP;
+ }
+ }
+
+ if (qctl.qc_type)
+ qctl.qc_type--;
+
+ if (qctl.qc_type == UGQUOTA) {
+ fprintf(stderr, "error: user and group quotas can't be set "
+ "both\n");
+ return CMD_HELP;
+ }
+
+ if (qctl.qc_cmd == LUSTRE_Q_SETQUOTA) {
+ struct obd_dqblk *dqb = &qctl.qc_dqblk;
+
+ if (optind + 6 != argc)
+ return CMD_HELP;
+
+ rc = name2id(&qctl.qc_id, argv[optind++], qctl.qc_type);
+ if (rc) {
+ fprintf(stderr, "error: find id for name %s failed: %s\n",
+ argv[optind - 1], strerror(errno));
+ return CMD_HELP;
+ }
+
+ ARG2INT(dqb->dqb_bsoftlimit, argv[optind++], "block-softlimit");
+ ARG2INT(dqb->dqb_bhardlimit, argv[optind++], "block-hardlimit");
+ ARG2INT(dqb->dqb_isoftlimit, argv[optind++], "inode-softlimit");
+ ARG2INT(dqb->dqb_ihardlimit, argv[optind++], "inode-hardlimit");
+
+ dqb->dqb_valid = QIF_LIMITS;
+ } else {
+ struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
+
+ if (optind + 3 != argc)
+ return CMD_HELP;
+
+ if ((dqi->dqi_bgrace = str2sec(argv[optind++])) == ULONG_MAX) {
+ fprintf(stderr, "error: bad %s: %s\n", "block-grace", argv[optind - 1]);
+ return CMD_HELP;
+ }
+ if ((dqi->dqi_igrace = str2sec(argv[optind++])) == ULONG_MAX) {
+ fprintf(stderr, "error: bad %s: %s\n", "inode-grace", argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ mnt = argv[optind];
+
+ rc = llapi_quotactl(mnt, &qctl);
+ if (rc) {
+ if (*obd_type)
+ fprintf(stderr, "%s %s ", obd_type, obd_uuid);
+ fprintf(stderr, "setquota failed: %s\n", strerror(errno));
+ return rc;
+ }
+
+ return 0;
+}
+
+static inline char *type2name(int check_type)
+{
+ if (check_type == USRQUOTA)
+ return "user";
+ else if (check_type == GRPQUOTA)
+ return "group";
+ else
+ return "unknown";
+}
+
+
+/* Converts seconds value into format string
+ * result is returned in buf
+ * Notes:
+ * 1. result is in descenting order: 1w2d3h4m5s
+ * 2. zero fields are not filled (except for p. 3): 5d1s
+ * 3. zero seconds value is presented as "0s"
+ */
+static void sec2str(time_t seconds, char *buf)
+{
+ const char spec[] = "smhdw";
+ const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
+ unsigned long c;
+ char* tail = buf;
+ int i;
+
+ for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
+ c = seconds / mult[i];
+
+ if (c > 0 || (i == 0 && buf == tail))
+ tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
+
+ seconds %= mult[i];
+ }
+}
+
+
+static void diff2str(time_t seconds, char *buf, time_t now)
+{
+
+ buf[0] = 0;
+ if (!seconds)
+ return;
+ if (seconds <= now) {
+ strcpy(buf, "none");
+ return;
+ }
+ sec2str(seconds - now, buf);
+}
+
+static void print_quota_title(char *name, struct if_quotactl *qctl)
+{
+ printf("Disk quotas for %s %s (%cid %u):\n",
+ type2name(qctl->qc_type), name,
+ *type2name(qctl->qc_type), qctl->qc_id);
+ printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
+ "Filesystem",
+ "kbytes", "quota", "limit", "grace",
+ "files", "quota", "limit", "grace");
+}
+
+static void print_quota(char *mnt, struct if_quotactl *qctl, int ost_only)
+{
+ time_t now;
+
+ time(&now);
+
+ if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
+ int bover = 0, iover = 0;
+ struct obd_dqblk *dqb = &qctl->qc_dqblk;
+
+ if (dqb->dqb_bhardlimit &&
+ toqb(dqb->dqb_curspace) > dqb->dqb_bhardlimit) {
+ bover = 1;
+ } else if (dqb->dqb_bsoftlimit &&
+ toqb(dqb->dqb_curspace) > dqb->dqb_bsoftlimit) {
+ if (dqb->dqb_btime > now) {
+ bover = 2;
+ } else {
+ bover = 3;
+ }
+ }
+
+ if (dqb->dqb_ihardlimit &&
+ dqb->dqb_curinodes > dqb->dqb_ihardlimit) {
+ iover = 1;
+ } else if (dqb->dqb_isoftlimit &&
+ dqb->dqb_curinodes > dqb->dqb_isoftlimit) {
+ if (dqb->dqb_btime > now) {
+ iover = 2;
+ } else {
+ iover = 3;
+ }
+ }
+
+#if 0 /* XXX: always print quotas even when no usages */
+ if (dqb->dqb_curspace || dqb->dqb_curinodes)
+#endif
+ {
+ char numbuf[3][32];
+ char timebuf[40];
+
+ if (strlen(mnt) > 15)
+ printf("%s\n%15s", mnt, "");
+ else
+ printf("%15s", mnt);
+
+ if (bover)
+ diff2str(dqb->dqb_btime, timebuf, now);
+
+ sprintf(numbuf[0], LPU64, toqb(dqb->dqb_curspace));
+ sprintf(numbuf[1], LPU64, dqb->dqb_bsoftlimit);
+ sprintf(numbuf[2], LPU64, dqb->dqb_bhardlimit);
+ printf(" %7s%c %6s %7s %7s",
+ numbuf[0], bover ? '*' : ' ', numbuf[1],
+ numbuf[2], bover > 1 ? timebuf : "");
+
+ if (iover)
+ diff2str(dqb->dqb_itime, timebuf, now);
+
+ sprintf(numbuf[0], LPU64, dqb->dqb_curinodes);
+ sprintf(numbuf[1], LPU64, dqb->dqb_isoftlimit);
+ sprintf(numbuf[2], LPU64, dqb->dqb_ihardlimit);
+ if (!ost_only)
+ printf(" %7s%c %6s %7s %7s",
+ numbuf[0], iover ? '*' : ' ', numbuf[1],
+ numbuf[2], iover > 1 ? timebuf : "");
+ printf("\n");
+ }
+ } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
+ qctl->qc_cmd == Q_GETOINFO) {
+ char bgtimebuf[40];
+ char igtimebuf[40];
+
+ sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf);
+ sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf);
+ printf("Block grace time: %s; Inode grace time: %s\n",
+ bgtimebuf, igtimebuf);
+ }
+}
+
+static void print_mds_quota(char *mnt, struct if_quotactl *qctl)
+{
+ int rc;
+
+ /* XXX: this is a flag to mark that only mds quota is wanted */
+ qctl->qc_dqblk.dqb_valid = 1;
+ rc = llapi_quotactl(mnt, qctl);
+ if (rc) {
+ fprintf(stderr, "quotactl failed: %s\n", strerror(errno));
+ return;
+ }
+ qctl->qc_dqblk.dqb_valid = 0;
+
+ print_quota(qctl->obd_uuid.uuid, qctl, 0);
+}
+
+static void print_lov_quota(char *mnt, struct if_quotactl *qctl)
+{
+ DIR *dir;
+ struct obd_uuid *uuids = NULL, *uuidp;
+ int obdcount = 1024;
+ int i, rc;
+
+ dir = opendir(mnt);
+ if (!dir) {
+ fprintf(stderr, "open %s failed: %s\n", mnt, strerror(errno));
+ return;
+ }
+
+ uuids = (struct obd_uuid *)malloc(INIT_ALLOC_NUM_OSTS *
+ sizeof(struct obd_uuid));
+ if (uuids == NULL)
+ goto out;
+
+retry_get_uuids:
+ rc = llapi_lov_get_uuids(dirfd(dir), uuids, &obdcount);
+ if (rc != 0) {
+ struct obd_uuid *uuids_temp;
+
+ if (rc == -EOVERFLOW) {
+ uuids_temp = realloc(uuids, obdcount *
+ sizeof(struct obd_uuid));
+ if (uuids_temp != NULL)
+ goto retry_get_uuids;
+ else
+ rc = -ENOMEM;
+ }
+
+ fprintf(stderr, "get ost uuid failed: %s\n", strerror(rc));
+ goto out;
+ }
+
+ for (i = 0, uuidp = uuids; i < obdcount; i++, uuidp++) {
+ memcpy(&qctl->obd_uuid, uuidp, sizeof(*uuidp));
+
+ /* XXX clear this flag to get quota from osts */
+ qctl->qc_dqblk.dqb_valid = 0;
+ rc = llapi_quotactl(mnt, qctl);
+ if (rc) {
+ fprintf(stderr, "%s quotactl failed: %s\n",
+ uuidp->uuid, strerror(errno));
+ continue;
+ }
+
+ print_quota(uuidp->uuid, qctl, 1);
+ }
+
+out:
+ closedir(dir);
+ return;