mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 01:30:30 -04:00
find: Fix -printf
Each -printf invocation created a memstream, then freed the underlying
buffer without closing the stream, resulting in a segfault on exit
when libc tried to flush all the streams. Drop the memstream, which
isn't really needed.
Furthermore:
* Change escape() to return char * instead of const char *
* Simplify the implementation of %h
* Fix %M, which printed an extra space
* Implement %l correctly
* Implement %Y and %y
* Add tests for everything except %S
Fixes: 7b9c912c41
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D51776
This commit is contained in:
parent
7378290edb
commit
02f394281f
3 changed files with 223 additions and 62 deletions
|
|
@ -1144,9 +1144,18 @@ Inode of the file.
|
|||
.It n
|
||||
Number of hard links.
|
||||
.It y
|
||||
Unimplemented -- Type of the file
|
||||
A single character representing the type of the file.
|
||||
.It Y
|
||||
Unimplemented -- Type of the file with loop detection
|
||||
A single character representing the type of the file.
|
||||
If the file is a symbolic link, show information for the target of the
|
||||
link instead, or
|
||||
.Sq L
|
||||
if the link loops,
|
||||
.Sq N
|
||||
if the target does not exist, or
|
||||
.Sq ?
|
||||
if any other error occurs while attempting to determine the type of
|
||||
the target.
|
||||
.It a
|
||||
Access time of the file.
|
||||
.It A
|
||||
|
|
|
|||
|
|
@ -5,15 +5,18 @@
|
|||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fts.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <fts.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "find.h"
|
||||
|
||||
|
|
@ -32,7 +35,7 @@ isesc(char c)
|
|||
return (c >= 'a' && c <= 'v' && esc[c - 'a'] != c);
|
||||
}
|
||||
|
||||
static const char *
|
||||
static char *
|
||||
escape(const char *str, bool *flush, bool *warned)
|
||||
{
|
||||
char c;
|
||||
|
|
@ -147,7 +150,6 @@ fp_strftime(FILE *fp, time_t t, char mod)
|
|||
|
||||
gmtime_r(&t, &tm);
|
||||
fmt[1] = mod;
|
||||
printf("fmt is '%s'\n", fmt);
|
||||
if (strftime(buffer, sizeof(buffer), fmt, &tm) == 0)
|
||||
errx(1, "Format bad or data too long for buffer"); /* Can't really happen ??? */
|
||||
fputs(buffer, fp);
|
||||
|
|
@ -156,36 +158,36 @@ fp_strftime(FILE *fp, time_t t, char mod)
|
|||
void
|
||||
do_printf(PLAN *plan, FTSENT *entry, FILE *fout)
|
||||
{
|
||||
const char *fmt, *path, *pend, *all;
|
||||
char c;
|
||||
FILE *fp;
|
||||
char buf[4096];
|
||||
struct stat sb;
|
||||
struct stat *sp;
|
||||
const char *path, *pend;
|
||||
char *all, *fmt;
|
||||
ssize_t ret;
|
||||
int c;
|
||||
bool flush, warned;
|
||||
struct stat *sb;
|
||||
char *tmp;
|
||||
size_t tmplen;
|
||||
|
||||
fp = open_memstream(&tmp, &tmplen);
|
||||
warned = (plan->flags & F_HAS_WARNED) != 0;
|
||||
all = fmt = escape(plan->c_data, &flush, &warned);
|
||||
if (warned)
|
||||
plan->flags |= F_HAS_WARNED;
|
||||
sb = entry->fts_statp;
|
||||
for (c = *fmt++; c; c = *fmt++) {
|
||||
sp = entry->fts_statp;
|
||||
if (c != '%') {
|
||||
putc(c, fp);
|
||||
putc(c, fout);
|
||||
continue;
|
||||
}
|
||||
c = *fmt++;
|
||||
/* Style(9) deviation: case order same as gnu find info doc */
|
||||
switch (c) {
|
||||
case '%':
|
||||
putc(c, fp);
|
||||
putc(c, fout);
|
||||
break;
|
||||
case 'p': /* Path to file */
|
||||
fputs(entry->fts_path, fp);
|
||||
fputs(entry->fts_path, fout);
|
||||
break;
|
||||
case 'f': /* filename w/o dirs */
|
||||
fputs(entry->fts_name, fp);
|
||||
fputs(entry->fts_name, fout);
|
||||
break;
|
||||
case 'h':
|
||||
/*
|
||||
|
|
@ -195,98 +197,139 @@ do_printf(PLAN *plan, FTSENT *entry, FILE *fout)
|
|||
path = entry->fts_path;
|
||||
pend = strrchr(path, '/');
|
||||
if (pend == NULL)
|
||||
putc('.', fp);
|
||||
else {
|
||||
char *t = malloc(pend - path + 1);
|
||||
memcpy(t, path, pend - path);
|
||||
t[pend - path] = '\0';
|
||||
fputs(t, fp);
|
||||
free(t);
|
||||
}
|
||||
putc('.', fout);
|
||||
else
|
||||
fwrite(path, pend - path, 1, fout);
|
||||
break;
|
||||
case 'P': /* file with command line arg rm'd -- HOW? fts_parent? */
|
||||
errx(1, "%%%c is unimplemented", c);
|
||||
case 'H': /* Command line arg -- HOW? */
|
||||
errx(1, "%%%c is unimplemented", c);
|
||||
case 'g': /* gid human readable */
|
||||
fputs(group_from_gid(sb->st_gid, 0), fp);
|
||||
fputs(group_from_gid(sp->st_gid, 0), fout);
|
||||
break;
|
||||
case 'G': /* gid numeric */
|
||||
fprintf(fp, "%d", sb->st_gid);
|
||||
fprintf(fout, "%d", sp->st_gid);
|
||||
break;
|
||||
case 'u': /* uid human readable */
|
||||
fputs(user_from_uid(sb->st_uid, 0), fp);
|
||||
fputs(user_from_uid(sp->st_uid, 0), fout);
|
||||
break;
|
||||
case 'U': /* uid numeric */
|
||||
fprintf(fp, "%d", sb->st_uid);
|
||||
fprintf(fout, "%d", sp->st_uid);
|
||||
break;
|
||||
case 'm': /* mode in octal */
|
||||
fprintf(fp, "%o", sb->st_mode & 07777);
|
||||
fprintf(fout, "%o", sp->st_mode & 07777);
|
||||
break;
|
||||
case 'M': { /* Mode in ls-standard form */
|
||||
char mode[12];
|
||||
strmode(sb->st_mode, mode);
|
||||
fputs(mode, fp);
|
||||
case 'M': /* Mode in ls-standard form */
|
||||
strmode(sp->st_mode, buf);
|
||||
fwrite(buf, 10, 1, fout);
|
||||
break;
|
||||
}
|
||||
case 'k': /* kbytes used by file */
|
||||
fprintf(fp, "%jd", (intmax_t)sb->st_blocks / 2);
|
||||
fprintf(fout, "%jd", (intmax_t)sp->st_blocks / 2);
|
||||
break;
|
||||
case 'b': /* blocks used by file */
|
||||
fprintf(fp, "%jd", (intmax_t)sb->st_blocks);
|
||||
fprintf(fout, "%jd", (intmax_t)sp->st_blocks);
|
||||
break;
|
||||
case 's': /* size in bytes of file */
|
||||
fprintf(fp, "%ju", (uintmax_t)sb->st_size);
|
||||
fprintf(fout, "%ju", (uintmax_t)sp->st_size);
|
||||
break;
|
||||
case 'S': /* sparseness of file */
|
||||
fprintf(fp, "%3.1f",
|
||||
(float)sb->st_blocks * 512 / (float)sb->st_size);
|
||||
fprintf(fout, "%3.1f",
|
||||
(float)sp->st_blocks * 512 / (float)sp->st_size);
|
||||
break;
|
||||
case 'd': /* Depth in tree */
|
||||
fprintf(fp, "%ld", entry->fts_level);
|
||||
fprintf(fout, "%ld", entry->fts_level);
|
||||
break;
|
||||
case 'D': /* device number */
|
||||
fprintf(fp, "%ju", (uintmax_t)sb->st_dev);
|
||||
fprintf(fout, "%ju", (uintmax_t)sp->st_dev);
|
||||
break;
|
||||
case 'F': /* Filesystem type */
|
||||
errx(1, "%%%c is unimplemented", c);
|
||||
case 'l': /* object of symbolic link */
|
||||
fprintf(fp, "%s", entry->fts_accpath);
|
||||
ret = readlink(entry->fts_accpath, buf, sizeof(buf));
|
||||
if (ret > 0)
|
||||
fwrite(buf, ret, 1, fout);
|
||||
break;
|
||||
case 'i': /* inode # */
|
||||
fprintf(fp, "%ju", (uintmax_t)sb->st_ino);
|
||||
fprintf(fout, "%ju", (uintmax_t)sp->st_ino);
|
||||
break;
|
||||
case 'n': /* number of hard links */
|
||||
fprintf(fp, "%ju", (uintmax_t)sb->st_nlink);
|
||||
fprintf(fout, "%ju", (uintmax_t)sp->st_nlink);
|
||||
break;
|
||||
case 'y': /* -type of file, incl 'l' */
|
||||
errx(1, "%%%c is unimplemented", c);
|
||||
case 'Y': /* -type of file, following 'l' types L loop ? error */
|
||||
errx(1, "%%%c is unimplemented", c);
|
||||
if (S_ISLNK(sp->st_mode)) {
|
||||
if (stat(entry->fts_accpath, &sb) != 0) {
|
||||
switch (errno) {
|
||||
case ELOOP:
|
||||
putc('L', fout);
|
||||
break;
|
||||
case ENOENT:
|
||||
putc('N', fout);
|
||||
break;
|
||||
default:
|
||||
putc('?', fout);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sp = &sb;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case 'y': /* -type of file, incl 'l' */
|
||||
switch (sp->st_mode & S_IFMT) {
|
||||
case S_IFIFO:
|
||||
putc('p', fout);
|
||||
break;
|
||||
case S_IFCHR:
|
||||
putc('c', fout);
|
||||
break;
|
||||
case S_IFDIR:
|
||||
putc('d', fout);
|
||||
break;
|
||||
case S_IFBLK:
|
||||
putc('b', fout);
|
||||
break;
|
||||
case S_IFREG:
|
||||
putc('f', fout);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
putc('l', fout);
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
putc('s', fout);
|
||||
break;
|
||||
case S_IFWHT:
|
||||
putc('w', fout);
|
||||
break;
|
||||
default:
|
||||
putc('U', fout);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'a': /* access time ctime */
|
||||
fp_ctime(fp, sb->st_atime);
|
||||
fp_ctime(fout, sp->st_atime);
|
||||
break;
|
||||
case 'A': /* access time with next char strftime format */
|
||||
fp_strftime(fp, sb->st_atime, *fmt++);
|
||||
fp_strftime(fout, sp->st_atime, *fmt++);
|
||||
break;
|
||||
case 'B': /* birth time with next char strftime format */
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
|
||||
if (sb->st_birthtime != 0)
|
||||
fp_strftime(fp, sb->st_birthtime, *fmt);
|
||||
if (sp->st_birthtime != 0)
|
||||
fp_strftime(fout, sp->st_birthtime, *fmt);
|
||||
#endif
|
||||
fmt++;
|
||||
break; /* blank on systems that don't support it */
|
||||
case 'c': /* status change time ctime */
|
||||
fp_ctime(fp, sb->st_ctime);
|
||||
fp_ctime(fout, sp->st_ctime);
|
||||
break;
|
||||
case 'C': /* status change time with next char strftime format */
|
||||
fp_strftime(fp, sb->st_ctime, *fmt++);
|
||||
fp_strftime(fout, sp->st_ctime, *fmt++);
|
||||
break;
|
||||
case 't': /* modification change time ctime */
|
||||
fp_ctime(fp, sb->st_mtime);
|
||||
fp_ctime(fout, sp->st_mtime);
|
||||
break;
|
||||
case 'T': /* modification time with next char strftime format */
|
||||
fp_strftime(fp, sb->st_mtime, *fmt++);
|
||||
fp_strftime(fout, sp->st_mtime, *fmt++);
|
||||
break;
|
||||
case 'Z': /* empty string for compat SELinux context string */
|
||||
break;
|
||||
|
|
@ -299,9 +342,7 @@ do_printf(PLAN *plan, FTSENT *entry, FILE *fout)
|
|||
errx(1, "Unknown format %c '%s'", c, all);
|
||||
}
|
||||
}
|
||||
fputs(tmp, fout);
|
||||
if (flush)
|
||||
fflush(fout);
|
||||
free(__DECONST(char *, fmt));
|
||||
free(tmp);
|
||||
free(all);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#-
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright 2017, Conrad Meyer <cem@FreeBSD.org>.
|
||||
# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
|
|
@ -64,8 +67,116 @@ find_samefile_link_body()
|
|||
atf_check -s exit:0 -o "inline:test/link2\n" find test -samefile test/link2
|
||||
}
|
||||
|
||||
atf_test_case find_printf
|
||||
find_printf_head()
|
||||
{
|
||||
atf_set "descr" "Test the -printf primary"
|
||||
}
|
||||
find_printf_body()
|
||||
{
|
||||
mkdir dir
|
||||
chmod 0755 dir
|
||||
jot -b hello 1024 >dir/file
|
||||
chmod 0644 dir/file
|
||||
ln -s file dir/link
|
||||
chmod -h 0444 dir/link
|
||||
local db=$(stat -f %b dir)
|
||||
local fb=$(stat -f %b dir/file)
|
||||
local lb=$(stat -f %b dir/link)
|
||||
|
||||
# paths
|
||||
atf_check -o inline:"dir\ndir/file\ndir/link\n" \
|
||||
find -s dir -printf '%p\n'
|
||||
atf_check -o inline:"dir\nfile\nlink\n" \
|
||||
find -s dir -printf '%f\n'
|
||||
atf_check -o inline:".\ndir\ndir\n" \
|
||||
find -s dir -printf '%h\n'
|
||||
atf_check -s exit:1 -e match:"unimplemented" -o ignore \
|
||||
find -s dir -printf '%P\n'
|
||||
atf_check -s exit:1 -e match:"unimplemented" -o ignore \
|
||||
find -s dir -printf '%H\n'
|
||||
|
||||
# group
|
||||
atf_check -o inline:"$(stat -f %Sg dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%g\n'
|
||||
atf_check -o inline:"$(stat -f %g dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%G\n'
|
||||
|
||||
# owner
|
||||
atf_check -o inline:"$(stat -f %Su dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%u\n'
|
||||
atf_check -o inline:"$(stat -f %u dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%U\n'
|
||||
|
||||
# mode
|
||||
atf_check -o inline:"$(stat -f %Lp dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%m\n'
|
||||
atf_check -o inline:"$(stat -f %Sp dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%M\n'
|
||||
|
||||
# size
|
||||
atf_check -o inline:"$((db/2))\n$((fb/2))\n$((lb/2))\n" \
|
||||
find -s dir -printf '%k\n'
|
||||
atf_check -o inline:"$db\n$fb\n$lb\n" \
|
||||
find -s dir -printf '%b\n'
|
||||
atf_check -o inline:"$(stat -f %z dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%s\n'
|
||||
# XXX test %S properly
|
||||
atf_check -o ignore \
|
||||
find -s dir -printf '%S\n'
|
||||
atf_check -o inline:"0\n1\n1\n" \
|
||||
find -s dir -printf '%d\n'
|
||||
|
||||
# device
|
||||
atf_check -o inline:"$(stat -f %d dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%D\n'
|
||||
atf_check -s exit:1 -e match:"unimplemented" -o ignore \
|
||||
find -s dir -printf '%F\n'
|
||||
|
||||
# link target
|
||||
atf_check -o inline:"\n\nfile\n" \
|
||||
find -s dir -printf '%l\n'
|
||||
|
||||
# inode
|
||||
atf_check -o inline:"$(stat -f %i dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%i\n'
|
||||
|
||||
# nlinks
|
||||
atf_check -o inline:"$(stat -f %l dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%n\n'
|
||||
|
||||
# type
|
||||
atf_check -o inline:"d\nf\nl\n" \
|
||||
find -s dir -printf '%y\n'
|
||||
atf_check -o inline:"d\nf\nf\n" \
|
||||
find -s dir -printf '%Y\n'
|
||||
|
||||
# access time
|
||||
atf_check -o inline:"$(stat -f %Sa -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%a\n'
|
||||
atf_check -o inline:"$(stat -f %Sa -t '%e' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%Ae\n'
|
||||
|
||||
# birth time
|
||||
atf_check -o inline:"$(stat -f %SB -t '%e' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%Be\n'
|
||||
|
||||
# inode change time
|
||||
atf_check -o inline:"$(stat -f %Sc -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%c\n'
|
||||
atf_check -o inline:"$(stat -f %Sc -t '%e' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%Ce\n'
|
||||
|
||||
# modification time
|
||||
atf_check -o inline:"$(stat -f %Sm -t '%a %b %e %T %Y' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%t\n'
|
||||
atf_check -o inline:"$(stat -f %Sm -t '%e' dir dir/file dir/link)\n" \
|
||||
find -s dir -printf '%Te\n'
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case find_newer_link
|
||||
atf_add_test_case find_samefile_link
|
||||
atf_add_test_case find_printf
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue