|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2005 Silicon Graphics, Inc. | 
|  | * All Rights Reserved. | 
|  | */ | 
|  |  | 
|  | #include <pwd.h> | 
|  | #include <grp.h> | 
|  | #include <ctype.h> | 
|  | #include "input.h" | 
|  | #include "command.h" | 
|  | #include "init.h" | 
|  | #include "quota.h" | 
|  |  | 
|  | static cmdinfo_t limit_cmd; | 
|  | static cmdinfo_t restore_cmd; | 
|  | static cmdinfo_t timer_cmd; | 
|  | static cmdinfo_t warn_cmd; | 
|  |  | 
|  | static void | 
|  | limit_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " modify quota limits for the specified user\n" | 
|  | "\n" | 
|  | " Example:\n" | 
|  | " 'limit bsoft=100m bhard=110m tanya\n" | 
|  | "\n" | 
|  | " Changes the soft and/or hard block limits, inode limits and/or realtime\n" | 
|  | " block limits that are currently being used for the specified user, group,\n" | 
|  | " or project.  The filesystem identified by the current path is modified.\n" | 
|  | " -d -- set the default values, used the first time a file is created\n" | 
|  | " -g -- modify group quota limits\n" | 
|  | " -p -- modify project quota limits\n" | 
|  | " -u -- modify user quota limits\n" | 
|  | " The block limit values can be specified with a units suffix - accepted\n" | 
|  | " units are: k (kilobytes), m (megabytes), g (gigabytes), and t (terabytes).\n" | 
|  | " The user/group/project can be specified either by name or by number.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | timer_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " modify quota enforcement timeout for the current filesystem\n" | 
|  | "\n" | 
|  | " Example:\n" | 
|  | " 'timer -i 3days'\n" | 
|  | " (soft inode limit timer is changed to 3 days)\n" | 
|  | "\n" | 
|  | " Changes the timeout value associated with the block limits, inode limits\n" | 
|  | " and/or realtime block limits for all users, groups, or projects on the\n" | 
|  | " current filesystem.\n" | 
|  | " As soon as a user consumes the amount of space or number of inodes set as\n" | 
|  | " the soft limit, a timer is started.  If the timer expires and the user is\n" | 
|  | " still over the soft limit, the soft limit is enforced as the hard limit.\n" | 
|  | " The default timeout is 7 days.\n" | 
|  | " -d -- set the default values, used the first time a file is created\n" | 
|  | " -g -- modify group quota timer\n" | 
|  | " -p -- modify project quota timer\n" | 
|  | " -u -- modify user quota timer\n" | 
|  | " -b -- modify the blocks-used timer\n" | 
|  | " -i -- modify the inodes-used timer\n" | 
|  | " -r -- modify the blocks-used timer for the (optional) realtime subvolume\n" | 
|  | " The timeout value is specified as a number of seconds, by default.\n" | 
|  | " However, a suffix may be used to alternatively specify minutes (m),\n" | 
|  | " hours (h), days (d), or weeks (w) - either the full word or the first\n" | 
|  | " letter of the word can be used.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | warn_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " modify the number of quota warnings sent to the specified user\n" | 
|  | "\n" | 
|  | " Example:\n" | 
|  | " 'warn 2 jimmy'\n" | 
|  | " (tell the quota system that two warnings have been sent to user jimmy)\n" | 
|  | "\n" | 
|  | " Changes the warning count associated with the block limits, inode limits\n" | 
|  | " and/or realtime block limits for the specified user, group, or project.\n" | 
|  | " When a user has been warned the maximum number of times allowed, the soft\n" | 
|  | " limit is enforced as the hard limit.  It is intended as an alternative to\n" | 
|  | " the timeout system, where the system administrator updates a count of the\n" | 
|  | " number of warnings issued to people, and they are penalised if the warnings\n" | 
|  | " are ignored.\n" | 
|  | " -d -- set maximum warning count, which triggers soft limit enforcement\n" | 
|  | " -g -- set group quota warning count\n" | 
|  | " -p -- set project quota warning count\n" | 
|  | " -u -- set user quota warning count\n" | 
|  | " -b -- set the blocks-used warning count\n" | 
|  | " -i -- set the inodes-used warning count\n" | 
|  | " -r -- set the blocks-used warn count for the (optional) realtime subvolume\n" | 
|  | " The user/group/project can be specified either by name or by number.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static uint32_t | 
|  | id_from_string( | 
|  | char	*name, | 
|  | int	type) | 
|  | { | 
|  | uint32_t	id = -1; | 
|  | const char	*type_name = "unknown type"; | 
|  |  | 
|  | switch (type) { | 
|  | case XFS_USER_QUOTA: | 
|  | type_name = "user"; | 
|  | id = uid_from_string(name); | 
|  | break; | 
|  | case XFS_GROUP_QUOTA: | 
|  | type_name = "group"; | 
|  | id = gid_from_string(name); | 
|  | break; | 
|  | case XFS_PROJ_QUOTA: | 
|  | type_name = "project"; | 
|  | id = prid_from_string(name); | 
|  | break; | 
|  | default: | 
|  | ASSERT(0); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (id == -1) { | 
|  | fprintf(stderr, _("%s: invalid %s name: %s\n"), | 
|  | type_name, progname, name); | 
|  | exitcode = 1; | 
|  | } | 
|  | return id; | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_limits( | 
|  | uint32_t	id, | 
|  | uint		type, | 
|  | uint		mask, | 
|  | char		*dev, | 
|  | uint64_t	*bsoft, | 
|  | uint64_t	*bhard, | 
|  | uint64_t	*isoft, | 
|  | uint64_t	*ihard, | 
|  | uint64_t	*rtbsoft, | 
|  | uint64_t	*rtbhard) | 
|  | { | 
|  | fs_disk_quota_t	d; | 
|  |  | 
|  | memset(&d, 0, sizeof(d)); | 
|  | d.d_version = FS_DQUOT_VERSION; | 
|  | d.d_id = id; | 
|  | d.d_flags = type; | 
|  | d.d_fieldmask = mask; | 
|  | d.d_blk_hardlimit = *bhard; | 
|  | d.d_blk_softlimit = *bsoft; | 
|  | d.d_ino_hardlimit = *ihard; | 
|  | d.d_ino_softlimit = *isoft; | 
|  | d.d_rtb_hardlimit = *rtbhard; | 
|  | d.d_rtb_softlimit = *rtbsoft; | 
|  |  | 
|  | if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: cannot set limits: %s\n"), | 
|  | progname, strerror(errno)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* extract number of blocks from an ascii string */ | 
|  | static int | 
|  | extractb( | 
|  | char		*string, | 
|  | const char	*prefix, | 
|  | int		length, | 
|  | uint		blocksize, | 
|  | uint		sectorsize, | 
|  | uint64_t	*value) | 
|  | { | 
|  | long long	v; | 
|  | char		*s = string; | 
|  |  | 
|  | if (strncmp(string, prefix, length) == 0) { | 
|  | s = string + length + 1; | 
|  | v = cvtnum(blocksize, sectorsize, s); | 
|  | if (v == -1LL) { | 
|  | fprintf(stderr, | 
|  | _("%s: Error: could not parse size %s.\n"), | 
|  | progname, s); | 
|  | return 0; | 
|  | } | 
|  | *value = (uint64_t)v >> 9;	/* syscalls use basic blocks */ | 
|  | if (v > 0 && *value == 0) | 
|  | fprintf(stderr, _("%s: Warning: `%s' in quota blocks is 0 (unlimited).\n"), progname, s); | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* extract number of inodes from an ascii string */ | 
|  | static int | 
|  | extracti( | 
|  | char		*string, | 
|  | const char	*prefix, | 
|  | int		length, | 
|  | uint64_t	*value) | 
|  | { | 
|  | char		*sp, *s = string; | 
|  |  | 
|  | if (strncmp(string, prefix, length) == 0) { | 
|  | s = string + length + 1; | 
|  | *value = strtoll(s, &sp, 0); | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | limit_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | char		*name; | 
|  | uint32_t	id; | 
|  | uint64_t	bsoft, bhard, isoft, ihard, rtbsoft, rtbhard; | 
|  | int		c, type = 0, mask = 0, flags = 0; | 
|  | uint		bsize, ssize, endoptions; | 
|  |  | 
|  | init_cvtnum(&bsize, &ssize); | 
|  | bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0; | 
|  | while ((c = getopt(argc, argv, "dgpu")) != EOF) { | 
|  | switch (c) { | 
|  | case 'd': | 
|  | flags |= DEFAULTS_FLAG; | 
|  | break; | 
|  | case 'g': | 
|  | type |= XFS_GROUP_QUOTA; | 
|  | break; | 
|  | case 'p': | 
|  | type |= XFS_PROJ_QUOTA; | 
|  | break; | 
|  | case 'u': | 
|  | type |= XFS_USER_QUOTA; | 
|  | break; | 
|  | default: | 
|  | return command_usage(&limit_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * In the usual case, we need at least 2 more arguments - | 
|  | * one (or more) limits and a user name/id. | 
|  | * For setting defaults (-d) we don't want a user name/id. | 
|  | */ | 
|  | if (flags & DEFAULTS_FLAG) { | 
|  | if (argc < optind + 1) | 
|  | return command_usage(&limit_cmd); | 
|  | endoptions = 1; | 
|  | } else if (argc < optind + 2) { | 
|  | return command_usage(&limit_cmd); | 
|  | } else { | 
|  | endoptions = 2; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Extract limit values from remaining optional arguments. | 
|  | */ | 
|  | while (argc > optind + endoptions - 1) { | 
|  | char *s = argv[optind++]; | 
|  | if (extractb(s, "bsoft=", 5, bsize, ssize, &bsoft)) | 
|  | mask |= FS_DQ_BSOFT; | 
|  | else if (extractb(s, "bhard=", 5, bsize, ssize, &bhard)) | 
|  | mask |= FS_DQ_BHARD; | 
|  | else if (extracti(s, "isoft=", 5, &isoft)) | 
|  | mask |= FS_DQ_ISOFT; | 
|  | else if (extracti(s, "ihard=", 5, &ihard)) | 
|  | mask |= FS_DQ_IHARD; | 
|  | else if (extractb(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft)) | 
|  | mask |= FS_DQ_RTBSOFT; | 
|  | else if (extractb(s, "rtbhard=", 7, bsize, ssize, &rtbhard)) | 
|  | mask |= FS_DQ_RTBHARD; | 
|  | else { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: unrecognised argument %s\n"), | 
|  | progname, s); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if (!mask) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: cannot find any valid arguments\n"), | 
|  | progname); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; | 
|  |  | 
|  | if (!type) { | 
|  | type = XFS_USER_QUOTA; | 
|  | } else if (type != XFS_GROUP_QUOTA && | 
|  | type != XFS_PROJ_QUOTA && | 
|  | type != XFS_USER_QUOTA) { | 
|  | return command_usage(&limit_cmd); | 
|  | } | 
|  |  | 
|  |  | 
|  | id = id_from_string(name, type); | 
|  | if (id == -1) | 
|  | return 0; | 
|  |  | 
|  | set_limits(id, type, mask, fs_path->fs_name, | 
|  | &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Iterate through input file, restoring the limits. | 
|  | * File format is as follows: | 
|  | * fs = <device> | 
|  | * <numeric id> bsoft bhard isoft ihard [rtbsoft rtbhard] | 
|  | */ | 
|  | static void | 
|  | restore_file( | 
|  | FILE		*fp, | 
|  | uint		type) | 
|  | { | 
|  | char		buffer[512]; | 
|  | char		dev[512]; | 
|  | uint		mask; | 
|  | int		cnt; | 
|  | uint32_t	id; | 
|  | uint64_t	bsoft, bhard, isoft, ihard, rtbsoft, rtbhard; | 
|  |  | 
|  | while (fgets(buffer, sizeof(buffer), fp) != NULL) { | 
|  | if (strncmp("fs = ", buffer, 5) == 0) { | 
|  | /* | 
|  | * Copy the device name to dev, strip off the trailing | 
|  | * newline, and move on to the next line. | 
|  | */ | 
|  | strncpy(dev, buffer + 5, sizeof(dev) - 1); | 
|  | dev[strlen(dev) - 1] = '\0'; | 
|  | continue; | 
|  | } | 
|  | rtbsoft = rtbhard = 0; | 
|  | cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n", | 
|  | &id, | 
|  | (unsigned long long *)&bsoft, | 
|  | (unsigned long long *)&bhard, | 
|  | (unsigned long long *)&isoft, | 
|  | (unsigned long long *)&ihard, | 
|  | (unsigned long long *)&rtbsoft, | 
|  | (unsigned long long *)&rtbhard); | 
|  | if (cnt == 5 || cnt == 7) { | 
|  | mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD; | 
|  | if (cnt == 7) | 
|  | mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD; | 
|  | set_limits(id, type, mask, dev, &bsoft, &bhard, | 
|  | &isoft, &ihard, &rtbsoft, &rtbhard); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | restore_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | FILE		*fp = stdin; | 
|  | char		*fname = NULL; | 
|  | int		c, type = 0; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "f:gpu")) != EOF) { | 
|  | switch (c) { | 
|  | case 'f': | 
|  | fname = optarg; | 
|  | break; | 
|  | case 'g': | 
|  | type |= XFS_GROUP_QUOTA; | 
|  | break; | 
|  | case 'p': | 
|  | type |= XFS_PROJ_QUOTA; | 
|  | break; | 
|  | case 'u': | 
|  | type |= XFS_USER_QUOTA; | 
|  | break; | 
|  | default: | 
|  | return command_usage(&restore_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (argc < optind) | 
|  | return command_usage(&restore_cmd); | 
|  |  | 
|  | if (!type) { | 
|  | type = XFS_USER_QUOTA; | 
|  | } else if (type != XFS_GROUP_QUOTA && | 
|  | type != XFS_PROJ_QUOTA && | 
|  | type != XFS_USER_QUOTA) { | 
|  | return command_usage(&restore_cmd); | 
|  | } | 
|  |  | 
|  | if (fname) { | 
|  | if ((fp = fopen(fname, "r")) == NULL) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: fopen on %s failed: %s\n"), | 
|  | progname, fname, strerror(errno)); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | restore_file(fp, type); | 
|  |  | 
|  | if (fname) | 
|  | fclose(fp); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | time64_t | 
|  | decode_timer( | 
|  | const struct fs_disk_quota *d, | 
|  | __s32			timer_lo, | 
|  | __s8			timer_hi) | 
|  | { | 
|  | if (d->d_fieldmask & FS_DQ_BIGTIME) | 
|  | return (uint32_t)timer_lo | (int64_t)timer_hi << 32; | 
|  | return timer_lo; | 
|  | } | 
|  |  | 
|  | static inline void | 
|  | encode_timer( | 
|  | const struct fs_disk_quota *d, | 
|  | __s32			*timer_lo, | 
|  | __s8			*timer_hi, | 
|  | time64_t		timer) | 
|  | { | 
|  | *timer_lo = timer; | 
|  | if (d->d_fieldmask & FS_DQ_BIGTIME) | 
|  | *timer_hi = timer >> 32; | 
|  | else | 
|  | *timer_hi = 0; | 
|  | } | 
|  |  | 
|  | static inline bool want_bigtime(time64_t timer) | 
|  | { | 
|  | return timer > INT32_MAX || timer < INT32_MIN; | 
|  | } | 
|  |  | 
|  | static void | 
|  | encode_timers( | 
|  | struct fs_disk_quota	*d, | 
|  | time64_t		btimer, | 
|  | time64_t		itimer, | 
|  | time64_t		rtbtimer) | 
|  | { | 
|  | d->d_fieldmask &= ~FS_DQ_BIGTIME; | 
|  | if (want_bigtime(btimer) || want_bigtime(itimer) || | 
|  | want_bigtime(rtbtimer)) | 
|  | d->d_fieldmask |= FS_DQ_BIGTIME; | 
|  |  | 
|  | encode_timer(d, &d->d_btimer, &d->d_btimer_hi, btimer); | 
|  | encode_timer(d, &d->d_itimer, &d->d_itimer_hi, itimer); | 
|  | encode_timer(d, &d->d_rtbtimer, &d->d_rtbtimer_hi, rtbtimer); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_timer( | 
|  | uint32_t		id, | 
|  | uint			type, | 
|  | uint			mask, | 
|  | char			*dev, | 
|  | time64_t		value) | 
|  | { | 
|  | struct fs_disk_quota	d; | 
|  | time64_t		btimer, itimer, rtbtimer; | 
|  |  | 
|  | memset(&d, 0, sizeof(d)); | 
|  |  | 
|  | /* | 
|  | * If id is specified we are extending grace time by value | 
|  | * Otherwise we are setting the default grace time | 
|  | */ | 
|  | if (id) { | 
|  | time_t	now; | 
|  |  | 
|  | /* Get quota to find out whether user is past soft limits */ | 
|  | if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: cannot get quota: %s\n"), | 
|  | progname, strerror(errno)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | time(&now); | 
|  |  | 
|  | btimer = decode_timer(&d, d.d_btimer, d.d_btimer_hi); | 
|  | itimer = decode_timer(&d, d.d_itimer, d.d_itimer_hi); | 
|  | rtbtimer = decode_timer(&d, d.d_rtbtimer, d.d_rtbtimer_hi); | 
|  |  | 
|  | /* Only set grace time if user is already past soft limit */ | 
|  | if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit) | 
|  | btimer = now + value; | 
|  | if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit) | 
|  | itimer = now + value; | 
|  | if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit) | 
|  | rtbtimer = now + value; | 
|  | } else { | 
|  | btimer = value; | 
|  | itimer = value; | 
|  | rtbtimer = value; | 
|  | } | 
|  |  | 
|  | d.d_version = FS_DQUOT_VERSION; | 
|  | d.d_flags = type; | 
|  | d.d_fieldmask = mask; | 
|  | d.d_id = id; | 
|  | encode_timers(&d, btimer, itimer, rtbtimer); | 
|  |  | 
|  | if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: cannot set timer: %s\n"), | 
|  | progname, strerror(errno)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | timer_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | time64_t	value; | 
|  | char		*name = NULL; | 
|  | uint32_t	id = 0; | 
|  | int		c, flags = 0, type = 0, mask = 0; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "bdgipru")) != EOF) { | 
|  | switch (c) { | 
|  | case 'd': | 
|  | flags |= DEFAULTS_FLAG; | 
|  | break; | 
|  | case 'b': | 
|  | mask |= FS_DQ_BTIMER; | 
|  | break; | 
|  | case 'i': | 
|  | mask |= FS_DQ_ITIMER; | 
|  | break; | 
|  | case 'r': | 
|  | mask |= FS_DQ_RTBTIMER; | 
|  | break; | 
|  | case 'g': | 
|  | type |= XFS_GROUP_QUOTA; | 
|  | break; | 
|  | case 'p': | 
|  | type |= XFS_PROJ_QUOTA; | 
|  | break; | 
|  | case 'u': | 
|  | type |= XFS_USER_QUOTA; | 
|  | break; | 
|  | default: | 
|  | return command_usage(&timer_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Older versions of the command did not accept -d|id|name, | 
|  | * so in that case we assume we're setting default timer, | 
|  | * and the last arg is the timer value. | 
|  | * | 
|  | * Otherwise, if the defaults flag is set, we expect 1 more arg for | 
|  | * timer value ; if not, 2 more args: 1 for value, one for id/name. | 
|  | */ | 
|  | if (!(flags & DEFAULTS_FLAG) && (argc == optind + 1)) { | 
|  | value = cvttime(argv[optind++]); | 
|  | } else if (flags & DEFAULTS_FLAG) { | 
|  | if (argc != optind + 1) | 
|  | return command_usage(&timer_cmd); | 
|  | value = cvttime(argv[optind++]); | 
|  | } else if (argc == optind + 2) { | 
|  | value = cvttime(argv[optind++]); | 
|  | name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; | 
|  | } else | 
|  | return command_usage(&timer_cmd); | 
|  |  | 
|  |  | 
|  | /* if none of -bir specified, set them all */ | 
|  | if (!mask) | 
|  | mask = FS_DQ_TIMER_MASK; | 
|  |  | 
|  | if (!type) { | 
|  | type = XFS_USER_QUOTA; | 
|  | } else if (type != XFS_GROUP_QUOTA && | 
|  | type != XFS_PROJ_QUOTA && | 
|  | type != XFS_USER_QUOTA) { | 
|  | return command_usage(&timer_cmd); | 
|  | } | 
|  |  | 
|  | if (name) | 
|  | id = id_from_string(name, type); | 
|  |  | 
|  | if (id == -1) | 
|  | return 0; | 
|  |  | 
|  | set_timer(id, type, mask, fs_path->fs_name, value); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_warnings( | 
|  | uint32_t	id, | 
|  | uint		type, | 
|  | uint		mask, | 
|  | char		*dev, | 
|  | uint		value) | 
|  | { | 
|  | fs_disk_quota_t	d; | 
|  |  | 
|  | memset(&d, 0, sizeof(d)); | 
|  | d.d_version = FS_DQUOT_VERSION; | 
|  | d.d_id = id; | 
|  | d.d_flags = type; | 
|  | d.d_fieldmask = mask; | 
|  | d.d_iwarns = value; | 
|  | d.d_bwarns = value; | 
|  | d.d_rtbwarns = value; | 
|  |  | 
|  | if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) { | 
|  | exitcode = 1; | 
|  | fprintf(stderr, _("%s: cannot set warnings: %s\n"), | 
|  | progname, strerror(errno)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | warn_f( | 
|  | int		argc, | 
|  | char		**argv) | 
|  | { | 
|  | char		*name; | 
|  | uint32_t	id; | 
|  | uint		value; | 
|  | int		c, flags = 0, type = 0, mask = 0; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "bdgipru")) != EOF) { | 
|  | switch (c) { | 
|  | case 'd': | 
|  | flags |= DEFAULTS_FLAG; | 
|  | break; | 
|  | case 'b': | 
|  | mask |= FS_DQ_BWARNS; | 
|  | break; | 
|  | case 'i': | 
|  | mask |= FS_DQ_IWARNS; | 
|  | break; | 
|  | case 'r': | 
|  | mask |= FS_DQ_RTBWARNS; | 
|  | break; | 
|  | case 'g': | 
|  | type |= XFS_GROUP_QUOTA; | 
|  | break; | 
|  | case 'p': | 
|  | type |= XFS_PROJ_QUOTA; | 
|  | break; | 
|  | case 'u': | 
|  | type |= XFS_USER_QUOTA; | 
|  | break; | 
|  | default: | 
|  | return command_usage(&warn_cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * In the usual case, we need at least 2 more arguments - | 
|  | * one (or more) value and a user name/id. | 
|  | * For setting defaults (-d) we don't want a user name/id. | 
|  | */ | 
|  | if (flags & DEFAULTS_FLAG) { | 
|  | if (argc != optind + 1) | 
|  | return command_usage(&warn_cmd); | 
|  | } else if (argc != optind + 2) { | 
|  | return command_usage(&warn_cmd); | 
|  | } | 
|  |  | 
|  | value = atoi(argv[optind++]); | 
|  | name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; | 
|  |  | 
|  | if (!mask) | 
|  | mask = FS_DQ_WARNS_MASK; | 
|  |  | 
|  | if (!type) { | 
|  | type = XFS_USER_QUOTA; | 
|  | } else if (type != XFS_GROUP_QUOTA && | 
|  | type != XFS_PROJ_QUOTA && | 
|  | type != XFS_USER_QUOTA) { | 
|  | return command_usage(&warn_cmd); | 
|  | } | 
|  |  | 
|  | id = id_from_string(name, type); | 
|  | if (id == -1) | 
|  | return 0; | 
|  |  | 
|  | set_warnings(id, type, mask, fs_path->fs_name, value); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | edit_init(void) | 
|  | { | 
|  | limit_cmd.name = "limit"; | 
|  | limit_cmd.cfunc = limit_f; | 
|  | limit_cmd.argmin = 2; | 
|  | limit_cmd.argmax = -1; | 
|  | limit_cmd.args = \ | 
|  | _("[-g|-p|-u] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name"); | 
|  | limit_cmd.oneline = _("modify quota limits"); | 
|  | limit_cmd.help = limit_help; | 
|  | limit_cmd.flags = CMD_FLAG_FOREIGN_OK; | 
|  |  | 
|  | restore_cmd.name = "restore"; | 
|  | restore_cmd.cfunc = restore_f; | 
|  | restore_cmd.argmin = 0; | 
|  | restore_cmd.argmax = -1; | 
|  | restore_cmd.args = _("[-g|-p|-u] [-f file]"); | 
|  | restore_cmd.oneline = _("restore quota limits from a backup file"); | 
|  | restore_cmd.flags = CMD_FLAG_FOREIGN_OK; | 
|  |  | 
|  | timer_cmd.name = "timer"; | 
|  | timer_cmd.cfunc = timer_f; | 
|  | timer_cmd.argmin = 1; | 
|  | timer_cmd.argmax = -1; | 
|  | timer_cmd.args = _("[-bir] [-g|-p|-u] value [-d|id|name]"); | 
|  | timer_cmd.oneline = _("set quota enforcement timeouts"); | 
|  | timer_cmd.help = timer_help; | 
|  | timer_cmd.flags = CMD_FLAG_FOREIGN_OK; | 
|  |  | 
|  | warn_cmd.name = "warn"; | 
|  | warn_cmd.cfunc = warn_f; | 
|  | warn_cmd.argmin = 2; | 
|  | warn_cmd.argmax = -1; | 
|  | warn_cmd.args = _("[-bir] [-g|-p|-u] value -d|id|name"); | 
|  | warn_cmd.oneline = _("get/set enforcement warning counter"); | 
|  | warn_cmd.help = warn_help; | 
|  |  | 
|  | if (expert) { | 
|  | add_command(&limit_cmd); | 
|  | add_command(&restore_cmd); | 
|  | add_command(&timer_cmd); | 
|  | add_command(&warn_cmd); | 
|  | } | 
|  | } |