Replace per-file libmagic strings with MIME types in v6 packages

libmagic strings are useful for humans but less so for computers,
in particular when you start having things like

     PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced

All very fascinating but that level of image detail does not need to be
in package metadata. The libmagic strings also make the exact output
quite dependent on the exact libmagic version. They obviously want to
improve their output but for rpm's purposes, this is unwanted
instability. MIME types are far more predictable and also machine
processable in a whole different level, and that is what we'll use
for v6 packages.

We need to still produce fully compatible v4 packages though, and there are
external tools that look at the file class data in that tag, so we can't
just reuse the tag for MIME in v6 either. Which means we need to
duplicate all this goo, annoyingly. We get to drop some of it in v7,
one day...

Besides the concrete dictionary + per-file index header tags, add a
header extension to fill in gaps of data where possible and build into
a consumable format and add --filemime query alias for it, all very
very similar to --fileclass.

Add/adjust tests to match and show the difference between v4 and v6:
v4 packages have fileclass but only extension-populated mime types,
and v6 is the exact opposite.

Fixes: #1096
This commit is contained in:
Panu Matilainen 2024-09-19 14:09:18 +03:00 committed by Florian Festi
parent 82bb832bc6
commit 61913883cc
12 changed files with 226 additions and 65 deletions

View File

@ -69,14 +69,17 @@ struct rpmfc_s {
vector<rpmfcAttr> atypes; /*!< known file attribute types */
vector<string> fn; /*!< (no. files) file names */
vector<string> fmime;/*!< (no. files) file mime types */
vector<string> ftype;/*!< (no. files) file types */
ARGV_t *fattrs; /*!< (no. files) file attribute tokens */
vector<rpm_color_t> fcolor; /*!< (no. files) file colors */
vector<rpmsid> fmdictx;/*!< (no. files) file mime dictionary indices */
vector<rpmsid> fcdictx;/*!< (no. files) file class dictionary indices */
vector<uint32_t> fddictx;/*!< (no. files) file depends dictionary start */
vector<uint32_t> fddictn;/*!< (no. files) file depends dictionary no. entries */
vector<uint32_t> ddictx; /*!< (no. dependencies) file->dependency mapping */
rpmstrPool cdict; /*!< file class dictionary */
rpmstrPool mdict; /*!< file class dictionary */
rpmfcFileDeps fileDeps; /*!< file dependency mapping */
fattrHash fahash; /*!< attr:file mapping */
@ -871,6 +874,7 @@ rpmfc rpmfcFree(rpmfc fc)
rpmdsFree(fd.dep);
rpmstrPoolFree(fc->cdict);
rpmstrPoolFree(fc->mdict);
rpmstrPoolFree(fc->pool);
delete fc;
@ -1232,16 +1236,19 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
fc->nfiles = argvCount(argv);
fc->fn.assign(fc->nfiles, "");
fc->ftype.assign(fc->nfiles, "");
fc->fmime.assign(fc->nfiles, "");
fc->fattrs = (ARGV_t *)xcalloc(fc->nfiles, sizeof(*fc->fattrs));
fc->fcolor.assign(fc->nfiles, 0);
fc->fcdictx.assign(fc->nfiles, 0);
fc->fmdictx.assign(fc->nfiles, 0);
/* Initialize the per-file dictionary indices. */
fc->fddictx.assign(fc->nfiles, 0);
fc->fddictn.assign(fc->nfiles, 0);
/* Build (sorted) file class dictionary. */
/* Build (sorted) file class and mime dictionaries. */
fc->cdict = rpmstrPoolCreate();
fc->mdict = rpmstrPoolCreate();
#pragma omp parallel
{
@ -1346,6 +1353,7 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
/* Add attributes based on file type and/or path */
rpmfcAttributes(fc, ix, ftype, fmime, s);
fc->fmime[ix] = fmime;
if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
fc->ftype[ix] = ftype;
@ -1364,8 +1372,10 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
/* Add to file class dictionary and index array */
for (int ix = 0; ix < fc->nfiles; ix++) {
const string & ftype = fc->ftype[ix];
const string & fmime = fc->fmime[ix];
/* Pool id's start from 1, for headers we want it from 0 */
fc->fcdictx[ix] = rpmstrPoolId(fc->cdict, ftype.c_str(), 1) - 1;
fc->fmdictx[ix] = rpmstrPoolId(fc->mdict, fmime.c_str(), 1) - 1;
if (ftype.empty())
fc->fwhite++;
@ -1379,6 +1389,7 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
exit:
/* No more additions after this, freeze pool to minimize memory use */
rpmstrPoolFreeze(fc->cdict, 0);
rpmstrPoolFreeze(fc->mdict, 0);
return rc;
}
@ -1690,14 +1701,27 @@ rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
/* Add per-file colors(#files) */
headerPutUint32(pkg->header, RPMTAG_FILECOLORS, fc->fcolor.data(), fc->nfiles);
/* Add classes(#classes) */
for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) {
headerPutString(pkg->header, RPMTAG_CLASSDICT,
rpmstrPoolStr(fc->cdict, id));
}
if (pkg->rpmver >= 6) {
/* Add mime types(#mime types) */
for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->mdict); id++) {
headerPutString(pkg->header, RPMTAG_MIMEDICT,
rpmstrPoolStr(fc->mdict, id));
}
/* Add per-file classes(#files) */
headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx.data(), fc->nfiles);
/* Add per-file mime types(#files) */
headerPutUint32(pkg->header, RPMTAG_FILEMIMEINDEX,
fc->fmdictx.data(), fc->nfiles);
} else {
/* Add classes(#classes) */
for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) {
headerPutString(pkg->header, RPMTAG_CLASSDICT,
rpmstrPoolStr(fc->cdict, id));
}
/* Add per-file classes(#files) */
headerPutUint32(pkg->header, RPMTAG_FILECLASS,
fc->fcdictx.data(), fc->nfiles);
}
/* Add dependency dictionary(#dependencies) */
if (!fc->ddictx.empty()) {

View File

@ -90,6 +90,7 @@ Dirnames | 1118 | string array | dirname(3) components of contained pat
Filedigestalgo | 5011 | int32 | ID of file digest algorithm. If missing, considered `0` for `md5`.
Longarchivesize | 271 | int64 | (Uncompressed) payload size when > 4GB.
Longsize | 5009 | int64 | Installed package size when > 4GB.
Mimedict | 5116 | int32 | Dictionary of MIME types, only >= v6.
Payloadcompressor | 1125 | string | Payload compressor name (as passed to rpmio `Fopen()`)
Payloadflags | 1126 | string | Payload compressor level (as passed to rpmio `Fopen()`)
Payloadformat | 1124 | string | Payload format (`cpio`)
@ -109,6 +110,7 @@ Filegroupname | 1040 | string array | Unix group name.
Fileinodes | 1096 | int32 array | Abstract inode number (hardlink calculation only).
Filelangs | 1097 | string array | Optional language of the file (eg man page translations)
Filelinktos | 1036 | string array | Symlink target for symlink files.
Filemimeindex | 5115 | int32 array | Index into MIME dictionary (see Mimedict tag), only >= v6.
Filemodes | 1030 | int16 array | Unix file mode.
Filemtimes | 1034 | int32 array | Unix file modification timestamp (aka mtime).
Filerdevs | 1033 | int16 array | Device ID (of device files)
@ -121,10 +123,10 @@ Longfilesizes | 5008 | int64 array | File size (when files > 4GB are present)
Tag Name | Value| Type | Description
--------------------|------|--------------|------------
Classdict | 1142 | string array | File class (libmagic string) dictionary
Classdict | 1142 | string array | File class (libmagic string) dictionary (only v4)
Dependsdict | 1145 | int32 array | File dependencies dictionary
Filecaps | 5010 | string array | `cap_to_text(3)` textual representation of file capabilities.
Fileclass | 1141 | int32 array | Index into Classdict
Fileclass | 1141 | int32 array | Index into Classdict (only v4)
Filecolors | 1140 | int32 array | File "color" - 1 for 32bit ELF, 2 for 64bit ELF and 0 otherwise
Filedependsn | 1144 | int32 array | Number of file dependencies in Dependsdict, starting from Filedependsx
Filedependsx | 1143 | int32 array | Index into Dependsdict denoting start of this file's dependencies.

View File

@ -237,12 +237,19 @@ rpm_color_t rpmfiColor(rpmfi fi);
rpm_color_t rpmfiFColor(rpmfi fi);
/** \ingroup rpmfi
* Return current file class from file info set iterator.
* Return current file class from file info set iterator (v4 packages).
* @param fi file info set iterator
* @return current file class, 0 on invalid
*/
const char * rpmfiFClass(rpmfi fi);
/** \ingroup rpmfi
* Return current file mime type from file info set iterator (v6 packages)
* @param fi file info set iterator
* @return current file mime type, 0 on invalid
*/
const char * rpmfiFMime(rpmfi fi);
/** \ingroup rpmfi
* Return current file depends dictionary from file info set iterator.
* @param fi file info set iterator

View File

@ -153,6 +153,7 @@ enum rpmfiFlags_e {
RPMFI_NOFILEFLAGS = (1 << 17),
RPMFI_NOFILESIGNATURES = (1 << 18),
RPMFI_NOVERITYSIGNATURES = (1 << 19),
RPMFI_NOFILEMIME = (1 << 20),
};
typedef rpmFlags rpmfiFlags;
@ -161,14 +162,14 @@ typedef rpmFlags rpmfiFlags;
(RPMFI_NOFILECLASS | RPMFI_NOFILELANGS | \
RPMFI_NOFILEMTIMES | RPMFI_NOFILERDEVS | \
RPMFI_NOFILESIGNATURES | RPMFI_NOVERITYSIGNATURES | \
RPMFI_NOFILEVERIFYFLAGS)
RPMFI_NOFILEVERIFYFLAGS | RPMFI_NOFILEMIME)
#define RPMFI_FLAGS_INSTALL \
(RPMFI_NOFILECLASS | RPMFI_NOFILEVERIFYFLAGS)
(RPMFI_NOFILECLASS | RPMFI_NOFILEVERIFYFLAGS | RPMFI_NOFILEMIME)
#define RPMFI_FLAGS_VERIFY \
(RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \
RPMFI_NOFILECOLORS)
RPMFI_NOFILECOLORS | RPMFI_NOFILEMIME)
#define RPMFI_FLAGS_QUERY \
(RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \
@ -181,7 +182,7 @@ typedef rpmFlags rpmfiFlags;
RPMFI_NOFILEDIGESTS | RPMFI_NOFILEMTIMES | RPMFI_NOFILERDEVS | \
RPMFI_NOFILEINODES | RPMFI_NOFILECOLORS | \
RPMFI_NOFILESIGNATURES | RPMFI_NOVERITYSIGNATURES | \
RPMFI_NOFILEVERIFYFLAGS | RPMFI_NOFILEFLAGS)
RPMFI_NOFILEVERIFYFLAGS | RPMFI_NOFILEFLAGS | RPMFI_NOFILEMIME)
#define RPMFI_FLAGS_ONLY_FILENAMES \
(RPMFI_FLAGS_FILETRIGGER | RPMFI_NOFILESTATES)
@ -401,13 +402,21 @@ rpm_loff_t rpmfilesFSize(rpmfiles fi, int ix);
rpm_color_t rpmfilesFColor(rpmfiles fi, int ix);
/** \ingroup rpmfiles
* Return file class from file info set.
* Return file class from file info set (v4 packages)
* @param fi file info set
* @param ix file index
* @return file class, 0 on invalid
*/
const char * rpmfilesFClass(rpmfiles fi, int ix);
/** \ingroup rpmfiles
* Return file mime type from file info set (v6 packages)
* @param fi file info set
* @param ix file index
* @return file mime type, 0 on invalid
*/
const char * rpmfilesFMime(rpmfiles fi, int ix);
/** \ingroup rpmfiles
* Return file depends dictionary from file info set.
* @param fi file info set

View File

@ -393,6 +393,9 @@ typedef enum rpmTag_e {
RPMTAG_PAYLOADSIZE = 5112, /* l */
RPMTAG_PAYLOADSIZEALT = 5113, /* l */
RPMTAG_RPMFORMAT = 5114, /* i */
RPMTAG_FILEMIMEINDEX = 5115, /* i[] */
RPMTAG_MIMEDICT = 5116, /* s[] */
RPMTAG_FILEMIMES = 5117, /* s[] extension */
RPMTAG_FIRSTFREE_TAG /*!< internal */
} rpmTag;

View File

@ -97,6 +97,10 @@ struct rpmfiles_s {
rpm_count_t ncdict; /*!< No. of class entries. */
uint32_t * fcdictx; /*!< File class dictionary index (header) */
char ** mdict; /*!< File mime dictionary (header) */
rpm_count_t nmdict; /*!< No. of mime entries. */
uint32_t * fmdictx; /*!< File mime dictionary index (header) */
uint32_t * ddict; /*!< File depends dictionary (header) */
rpm_count_t nddict; /*!< No. of depends entries. */
uint32_t * fddictx; /*!< File depends dictionary start (header) */
@ -689,6 +693,19 @@ const char * rpmfilesFClass(rpmfiles fi, int ix)
return fclass;
}
const char * rpmfilesFMime(rpmfiles fi, int ix)
{
const char * fmime = NULL;
int mdictx;
if (fi != NULL && fi->fmdictx != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
mdictx = fi->fmdictx[ix];
if (fi->mdict != NULL && mdictx >= 0 && mdictx < fi->nmdict)
fmime = fi->mdict[mdictx];
}
return fmime;
}
uint32_t rpmfilesFDepends(rpmfiles fi, int ix, const uint32_t ** fddictp)
{
int fddictx = -1;
@ -1244,6 +1261,7 @@ rpmfiles rpmfilesFree(rpmfiles fi)
fi->fcaps = _free(fi->fcaps);
fi->cdict = _free(fi->cdict);
fi->mdict = _free(fi->mdict);
fi->fuser = _free(fi->fuser);
fi->fgroup = _free(fi->fgroup);
@ -1264,6 +1282,7 @@ rpmfiles rpmfilesFree(rpmfiles fi)
fi->fcolors = _free(fi->fcolors);
fi->fcdictx = _free(fi->fcdictx);
fi->fmdictx = _free(fi->fmdictx);
fi->ddict = _free(fi->ddict);
fi->fddictx = _free(fi->fddictx);
fi->fddictn = _free(fi->fddictn);
@ -1590,6 +1609,11 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
fi->ncdict = rpmtdCount(&td);
_hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
}
if (!(flags & RPMFI_NOFILEMIME)) {
_hgfinc(h, RPMTAG_MIMEDICT, &td, scareFlags, fi->mdict);
fi->nmdict = rpmtdCount(&td);
_hgfi(h, RPMTAG_FILEMIMEINDEX, &td, scareFlags, fi->fmdictx);
}
if (!(flags & RPMFI_NOFILEDEPS)) {
_hgfinc(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
fi->nddict = rpmtdCount(&td);
@ -1870,6 +1894,7 @@ RPMFI_ITERFUNC(const char *, FGroup, i)
RPMFI_ITERFUNC(const char *, FCaps, i)
RPMFI_ITERFUNC(const char *, FLangs, i)
RPMFI_ITERFUNC(const char *, FClass, i)
RPMFI_ITERFUNC(const char *, FMime, i)
RPMFI_ITERFUNC(rpmfileState, FState, i)
RPMFI_ITERFUNC(rpmfileAttrs, FFlags, i)
RPMFI_ITERFUNC(rpmVerifyAttrs, VFlags, i)

View File

@ -511,6 +511,50 @@ static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags)
return ftypeTag(h, td, hgflags, makeFClass);
}
/*
* Attempt to generate file mime type if missing from header:
* we can easily generate this for symlinks and other special types.
* Always return malloced strings to simplify life in filemimeTag().
*/
static char *makeFMime(rpmfi fi)
{
char *fmime = NULL;
const char *hm = rpmfiFMime(fi);
if (hm != NULL && hm[0] != '\0') {
fmime = xstrdup(hm);
} else {
switch (rpmfiFMode(fi) & S_IFMT) {
case S_IFBLK:
fmime = xstrdup("inode/blockdevice");
break;
case S_IFCHR:
fmime = xstrdup("inode/chardevice");
break;
case S_IFDIR:
fmime = xstrdup("inode/directory");
break;
case S_IFIFO:
fmime = xstrdup("inode/fifo");
break;
case S_IFSOCK:
fmime = xstrdup("inode/socket");
break;
case S_IFLNK:
fmime = xstrdup("inode/symlink");
break;
}
}
return (fmime != NULL) ? fmime : xstrdup("");
}
/* Retrieve/generate file mime types */
static int filemimesTag(Header h, rpmtd td, headerGetFlags hgflags)
{
return ftypeTag(h, td, hgflags, makeFMime);
}
/**
* Retrieve file provides.
* @param h header
@ -1048,6 +1092,7 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
{ RPMTAG_CONFLICTNEVRS, conflictnevrsTag },
{ RPMTAG_FILENLINKS, filenlinksTag },
{ RPMTAG_SYSUSERS, sysusersTag },
{ RPMTAG_FILEMIMES, filemimesTag },
{ 0, NULL }
};

View File

@ -149,6 +149,9 @@ rpm alias --filesbypkg --qf '[%-25{=NAME} %{FILENAMES}\n]' \
rpm alias --fileclass --qf '[%{FILENAMES}\t%{FILECLASS}\n]' \
--POPTdesc=$"list file names with their classes"
rpm alias --filemime --qf '[%{FILENAMES}\t%{FILEMIMES}\n]' \
--POPTdesc=$"list file names with their mime types"
rpm alias --filecolor --qf '[%{FILENAMES}\t%{FILECOLORS}\n]' \
--POPTdesc=$"list file names with their colors"

View File

@ -43,8 +43,8 @@ Tag #2 [region]
Header:
Header magic: 1e8ad8e (reserved: 0)
Index entries: 56 (896 bytes)
Data size: 2716 bytes
Header size: 3612 bytes
Data size: 2719 bytes
Header size: 3615 bytes
Region entries 56
Region size 912
@ -53,7 +53,7 @@ Dribbles: 0
Tag #0 [region]
tagno: 63 (Headerimmutable)
type: 7 (blob)
offset: 2700
offset: 2703
count: 16
region trailer
@ -321,74 +321,74 @@ Tag #43 [region]
count: 20
Tag #44 [region]
tagno: 1141 (Fileclass)
type: 4 (int32)
offset: 2252
tagno: 5008 (Longfilesizes)
type: 5 (int64)
offset: 2256
count: 20
Tag #45 [region]
tagno: 1142 (Classdict)
type: 8 (argv)
offset: 2332
count: 2
Tag #46 [region]
tagno: 5008 (Longfilesizes)
type: 5 (int64)
offset: 2360
count: 20
Tag #47 [region]
tagno: 5009 (Longsize)
type: 5 (int64)
offset: 2520
offset: 2416
count: 1
Tag #46 [region]
tagno: 5011 (Filedigestalgo)
type: 4 (int32)
offset: 2424
count: 1
Tag #47 [region]
tagno: 5062 (Encoding)
type: 6 (string)
offset: 2428
count: 1
Tag #48 [region]
tagno: 5011 (Filedigestalgo)
type: 4 (int32)
offset: 2528
tagno: 5092 (Payloaddigest)
type: 8 (argv)
offset: 2434
count: 1
Tag #49 [region]
tagno: 5062 (Encoding)
type: 6 (string)
offset: 2532
tagno: 5093 (Payloaddigestalgo)
type: 4 (int32)
offset: 2500
count: 1
Tag #50 [region]
tagno: 5092 (Payloaddigest)
tagno: 5097 (Payloaddigestalt)
type: 8 (argv)
offset: 2538
offset: 2504
count: 1
Tag #51 [region]
tagno: 5093 (Payloaddigestalgo)
type: 4 (int32)
offset: 2604
tagno: 5112 (Payloadsize)
type: 5 (int64)
offset: 2576
count: 1
Tag #52 [region]
tagno: 5097 (Payloaddigestalt)
type: 8 (argv)
offset: 2608
tagno: 5113 (Payloadsizealt)
type: 5 (int64)
offset: 2584
count: 1
Tag #53 [region]
tagno: 5112 (Payloadsize)
type: 5 (int64)
offset: 2680
tagno: 5114 (Rpmformat)
type: 4 (int32)
offset: 2592
count: 1
Tag #54 [region]
tagno: 5113 (Payloadsizealt)
type: 5 (int64)
offset: 2688
count: 1
tagno: 5115 (Filemimeindex)
type: 4 (int32)
offset: 2596
count: 20
Tag #55 [region]
tagno: 5114 (Rpmformat)
type: 4 (int32)
offset: 2696
count: 1
tagno: 5116 (Mimedict)
type: 8 (argv)
offset: 2676
count: 2

View File

@ -1,4 +1,4 @@
SHA256HEADER: c62e9ba20111e91422c7cfb4c78b6e080ae5a8cfbc567dfd6e376e128d530baa
SHA256HEADER: ac31b7e4d92a0fd5d98099feb2aa0d4bd41517a8d11d16f8c0802275228a334f
SHA1HEADER: (none)
SIGMD5: (none)
PAYLOADDIGEST: 2f916d301d47ee34f16b74c7c49d11448fceb5487c6ba53c04950423fffeab95

View File

@ -120,6 +120,8 @@ FILEINODES
FILELANGS
FILELINKTOS
FILEMD5S
FILEMIMEINDEX
FILEMIMES
FILEMODES
FILEMTIMES
FILENAMES
@ -164,6 +166,7 @@ LONGARCHIVESIZE
LONGFILESIZES
LONGSIGSIZE
LONGSIZE
MIMEDICT
MODULARITYLABEL
N
NAME

View File

@ -1341,13 +1341,16 @@ Sourcepackage
[])
RPMTEST_CLEANUP
# my autotest fu fails to deal with trailing whitespace except by trimming it
AT_SETUP([file classes query])
AT_KEYWORDS([query])
RPMDB_INIT
RPMTEST_CHECK([
runroot rpmbuild -bb --quiet /data/SPECS/filetypes.spec
runroot rpm -qp --fileclass /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm
],
RPMTEST_CHECK([[
# v4 packages
runroot rpmbuild -bb --quiet \
--define "_rpmfilever 4" /data/SPECS/filetypes.spec
runroot rpm -qp --fileclass /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm | sed -e 's/[[:space:]]*$//'
]],
[0],
[/opt/README ASCII text
/opt/linkme symbolic link to `myscript.sh'
@ -1355,4 +1358,41 @@ runroot rpm -qp --fileclass /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm
/opt/myscript.sh POSIX shell script, ASCII text executable
],
[])
RPMTEST_CHECK([[
runroot rpm -qp --filemime /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm | sed -e 's/[[:space:]]*$//'
]],
[0],
[/opt/README
/opt/linkme inode/symlink
/opt/mydir inode/directory
/opt/myscript.sh
],
[])
# v6 variants of the same
RPMTEST_CHECK([[
runroot rpmbuild -bb --quiet \
--define "_rpmfilever 6" \
/data/SPECS/filetypes.spec
runroot rpm -qp --fileclass /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm | sed -e 's/[[:space:]]*$//'
]],
[0],
[/opt/README
/opt/linkme symbolic link to `myscript.sh'
/opt/mydir directory
/opt/myscript.sh
],
[])
RPMTEST_CHECK([[
runroot rpm -qp --filemime /build/RPMS/noarch/filetypes-1.0-1.noarch.rpm | sed -e 's/[[:space:]]*$//'
]],
[0],
[/opt/README text/plain
/opt/linkme inode/symlink
/opt/mydir inode/directory
/opt/myscript.sh text/x-shellscript
],
[])
RPMTEST_CLEANUP