quot: Rewrite -n mode input parser

The existing parser was needlessly complicated and wildly inconsistent
in how it handled invalid input.  Rewrite using getline() and treat
invalid input consistently: silently ignore lines that don't begin with
a number, and print a warning if the inode number is out of range.

PR:		290992
MFC after:	1 week
Reviewed by:	obrien
Differential Revision:	https://reviews.freebsd.org/D53726
This commit is contained in:
Dag-Erling Smørgrav
2025-11-14 15:28:40 +01:00
parent b0cfa27c91
commit fa272a5276
3 changed files with 46 additions and 23 deletions
+2 -1
View File
@@ -27,7 +27,7 @@
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd October 15, 2025
.Dd November 13, 2025
.Dt QUOT 8
.Os
.Sh NAME
@@ -60,6 +60,7 @@ By default, all sizes are reported in 512-byte block counts.
Given a list of inodes (plus some optional data on each line)
in the standard input, for each file print out the owner (plus
the remainder of the input line).
Lines that do not begin with a number are ignored.
This is traditionally used
in the pipe:
.Bd -literal -offset indent
+25 -22
View File
@@ -41,6 +41,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fstab.h>
#include <inttypes.h>
#include <libufs.h>
#include <mntopts.h>
#include <paths.h>
@@ -390,41 +391,43 @@ douser(int fd, struct fs *super)
static void
donames(int fd, struct fs *super)
{
int c;
ino_t maxino;
uintmax_t inode;
union dinode *dp;
char *end, *line;
size_t cap;
ssize_t len;
intmax_t inode, maxino;
maxino = super->fs_ncg * super->fs_ipg - 1;
/* first skip the name of the filesystem */
while ((c = getchar()) != EOF && (c < '0' || c > '9'))
while ((c = getchar()) != EOF && c != '\n');
ungetc(c, stdin);
while (scanf("%ju", &inode) == 1) {
if (inode > maxino) {
warnx("illegal inode %ju", inode);
return;
line = NULL;
cap = 0;
while ((len = getline(&line, &cap, stdin)) > 0) {
if (len > 0 && line[len - 1] == '\n')
line[--len] = '\0';
inode = strtoimax(line, &end, 10);
/*
* Silently ignore lines that do not begin with a number.
* For backward compatibility reasons, we do not require
* the optional comment to be preceded by whitespace.
*/
if (end == line)
continue;
if (inode <= 0 || inode > maxino) {
warnx("invalid inode %jd", inode);
continue;
}
if ((dp = get_inode(fd, super, inode)) != NULL &&
!isfree(super, dp)) {
printf("%s\t", user(DIP(super, dp, di_uid))->name);
/* now skip whitespace */
while ((c = getchar()) == ' ' || c == '\t')
/* nothing */;
while (*end == ' ' || *end == '\t')
end++;
/* and print out the remainder of the input line */
while (c != EOF && c != '\n') {
putchar(c);
c = getchar();
}
putchar('\n');
printf("%s\n", end);
} else {
/* skip this line */
while ((c = getchar()) != EOF && c != '\n')
/* nothing */;
}
if (c == EOF)
break;
}
free(line);
}
static void
+19
View File
@@ -15,6 +15,8 @@ quot_setup()
atf_check mount /dev/$dev "$mnt"
echo "/dev/$dev: ($mnt)" >expect
printf "%5d\t%5d\t%-8s\n" 8 2 "#0" >>expect
printf "%s\n" "/dev/$dev" >ninput
echo "/dev/$dev: ($mnt)" >nexpect
}
# Create a directory owned by a given UID
@@ -23,12 +25,25 @@ quot_adduid()
local uid=$1
atf_check install -d -o $uid -g 0 mnt/$uid
printf "%5d\t%5d\t%-8s\n" 4 1 "#$uid" >>expect
ls -di mnt/$uid >>ninput
printf "%s\t%s\n" "#$uid" mnt/$uid >>nexpect
}
# Perform the tests
quot_test()
{
local dev=$(cat dev)
# Deliberately add invalid lines to our -n input before the
# valid ones to verify that quot does not abort on first
# error. Note that quot deliberately ignores initial lines
# that don't start with a number, and that after encountering
# at least one line that does start with a number, quot would
# previously terminate on encountering one that doesn't (now
# it simply ignores them). This also tests that we don't
# require whitespace between the inode number and the comment.
echo "0zero" >>ninput
echo "invalid" >>ninput
echo "-1minusone" >>ninput
# Create inodes owned by a large number of users to exercise
# hash collisions and rehashing. The code uses an open hash
# table that starts out with only 8 entries and doubles every
@@ -50,6 +65,10 @@ quot_test()
atf_check mount -ur /dev/$dev
atf_check -o file:expect quot -fkN /dev/$dev
atf_check -o file:expect quot -fkN $(realpath mnt)
# Test -n option
atf_check -o file:nexpect \
-e inline:"quot: invalid inode 0\nquot: invalid inode -1\n" \
quot -Nn /dev/$dev <ninput
}
# Unmount and release the memory disk