/*
* unix.c
* Detection of general Unix file systems
*
* Copyright (c) 2003 Christoph Pfisterer
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "global.h"
/*
* JFS (for Linux)
*/
void detect_jfs(SECTION *section, int level)
{
unsigned char *buf;
int version;
char s[256];
u4 blocksize;
u8 blockcount;
if (get_buffer(section, 32768, 512, (void **)&buf) < 512)
return;
/* check signature */
if (memcmp(buf, "JFS1", 4) != 0)
return;
/* tell the user */
version = get_le_long(buf + 4);
print_line(level, "JFS file system, version %d", version);
get_string(buf + 101, 11, s);
print_line(level + 1, "Volume name \"%s\"", s);
blocksize = get_le_long(buf + 24);
blockcount = get_le_quad(buf + 8);
format_blocky_size(s, blockcount, blocksize, "h/w blocks", NULL);
print_line(level + 1, "Volume size %s", s);
}
/*
* XFS
*/
void detect_xfs(SECTION *section, int level)
{
unsigned char *buf;
u4 raw_version, blocksize;
u8 blockcount;
int version;
char s[256];
if (get_buffer(section, 0, 512, (void **)&buf) < 512)
return;
/* check signature */
if (get_be_long(buf) != 0x58465342)
return;
/* tell the user */
raw_version = get_be_short(buf + 0x64);
version = raw_version & 0x000f;
print_line(level, "XFS file system, version %d", version);
get_string(buf + 0x6c, 12, s);
print_line(level + 1, "Volume name \"%s\"", s);
format_uuid(buf + 32, s);
print_line(level + 1, "UUID %s", s);
blocksize = get_be_long(buf + 4);
blockcount = get_be_quad(buf + 8);
format_blocky_size(s, blockcount, blocksize, "blocks", NULL);
print_line(level + 1, "Volume size %s", s);
}
/*
* UFS file system from various BSD strains
*/
void detect_ufs(SECTION *section, int level)
{
unsigned char *buf;
int i, at, en, namelen, offsets[5] = { 0, 8, 64, 256, -1 };
u4 magic;
char s[256];
/* NextStep/OpenStep apparently can move the superblock further into
the device. Linux uses steps of 8 blocks (of the applicable block
size) to scan for it. We only scan at the canonical location for now. */
/* Possible block sizes: 512 (old formats), 1024 (most), 2048 (CD media) */
for (i = 0; offsets[i] >= 0; i++) {
at = offsets[i];
if (get_buffer(section, at * 1024, 1536, (void **)&buf) < 1536)
break;
for (en = 0; en < 2; en++) {
magic = get_ve_long(en, buf + 1372);
if (magic == 0x00011954) {
print_line(level, "UFS file system, %d KiB offset, %s",
at, get_ve_name(en));
} else if (magic == 0x00095014) {
print_line(level, "UFS file system, %d KiB offset, long file names, %s",
at, get_ve_name(en));
} else if (magic == 0x00195612) {
print_line(level, "UFS file system, %d KiB offset, fs_featurebits, %s",
at, get_ve_name(en));
} else if (magic == 0x05231994) {
print_line(level, "UFS file system, %d KiB offset, fs_featurebits, >4GB support, %s",
at, get_ve_name(en));
} else if (magic == 0x19540119) {
print_line(level, "UFS2 file system, %d KiB offset, %s",
at, get_ve_name(en));
} else
continue;
/* volume name by FreeBSD convention */
get_string(buf + 680, 32, s);
if (s[0])
print_line(level + 1, "Volume name \"%s\" (in superblock)", s);
/* last mount point */
get_string(buf + 212, 255, s); /* actually longer, but varies */
if (s[0])
print_line(level + 1, "Last mounted at \"%s\"", s);
/* volume name by Darwin convention */
if (get_buffer(section, 7 * 1024, 1024, (void **)&buf) == 1024) {
if (get_ve_long(en, buf) == 0x4c41424c && /* "LABL" */
get_ve_long(en, buf + 8) == 1) { /* version 1 */
namelen = get_ve_short(en, buf + 16);
get_string(buf + 18, namelen, s); /* automatically limits to 255 */
print_line(level + 1, "Volume name \"%s\" (in label v%lu)",
s, get_ve_long(en, buf + 8));
}
}
return;
}
}
}
/*
* System V file system
*/
void detect_sysv(SECTION *section, int level)
{
unsigned char *buf;
int i, at, en, offsets[5] = { 512, 1024, -1 };
s4 blocksize_code;
char s[256];
for (i = 0; offsets[i] >= 0; i++) {
at = offsets[i];
if (get_buffer(section, at, 1024, (void **)&buf) < 1024)
break;
for (en = 0; en < 2; en++) {
if (get_ve_long(en, buf + 1016) == 0x2b5544) {
blocksize_code = get_ve_long(en, buf + 1020);
s[0] = 0;
if (blocksize_code == 1)
strcpy(s, "512 byte blocks");
else if (blocksize_code == 2)
strcpy(s, "1 KiB blocks");
else if (blocksize_code == 3)
strcpy(s, "2 KiB blocks");
else
snprintf(s, 255, "unknown block size code %d", (int)blocksize_code);
print_line(level, "XENIX file system (SysV variant), %s, %s",
get_ve_name(en), s);
return;
}
if (get_ve_long(en, buf + 504) == 0xfd187e20) {
blocksize_code = get_ve_long(en, buf + 508);
s[0] = 0;
if (blocksize_code == 1)
strcpy(s, "512 byte blocks");
else if (blocksize_code == 2)
strcpy(s, "1 KiB blocks");
else
snprintf(s, 255, "unknown block size code %d", (int)blocksize_code);
print_line(level, "SysV file system, %s, %s",
get_ve_name(en), s);
return;
}
}
}
}
/*
* BSD disklabel
*/
static char * bsdtype_names[] = {
"Unused",
"swap",
"Sixth Edition",
"Seventh Edition",
"System V",
"V7 with 1 KiB blocks",
"Eighth Edition, 4 KiB blocks",
"4.2BSD fast file system",
"ext2 or MS-DOS",
"4.4BSD log-structured file system",
"\"Other\"",
"HPFS",
"ISO9660",
"bootstrap",
"AmigaDOS fast file system",
"Macintosh HFS",
"Digital Unix AdvFS",
};
static char * get_name_for_bsdtype(int type)
{
if (type >= 0 && type <= 16)
return bsdtype_names[type];
return "Unknown";
}
void detect_bsd_disklabel(SECTION *section, int level)
{
unsigned char *buf;
int i, off, partcount, types[16], min_offset_valid, did_recurse;
u4 starts[16], sizes[16];
u4 sectsize, nsectors, ntracks, ncylinders, secpercyl, secperunit;
u8 offset, min_offset, base_offset;
char s[256], append[64], pn;
if (section->flags & FLAG_IN_DISKLABEL)
return;
if (get_buffer(section, 512, 512, (void **)&buf) < 512)
return;
if (get_le_long(buf) != 0x82564557 ||
get_le_long(buf + 132) != 0x82564557)
return;
sectsize = get_le_long(buf + 40);
nsectors = get_le_long(buf + 44);
ntracks = get_le_long(buf + 48);
ncylinders = get_le_long(buf + 52);
secpercyl = get_le_long(buf + 56);
secperunit = get_le_long(buf + 60);
partcount = get_le_short(buf + 138);
if (partcount <= 8) {
print_line(level, "BSD disklabel (at sector 1), %d partitions", partcount);
} else if (partcount > 8 && partcount <= 16) {
print_line(level, "BSD disklabel (at sector 1), %d partitions (more than usual, but valid)",
partcount);
} else if (partcount > 16) {
print_line(level, "BSD disklabel (at sector 1), %d partitions (broken, limiting to 16)",
partcount);
partcount = 16;
}
if (sectsize != 512) {
print_line(level + 1, "Unusual sector size %d bytes, your mileage may vary");
}
min_offset = 0;
min_offset_valid = 0;
for (i = 0, off = 148; i < partcount; i++, off += 16) {
starts[i] = get_le_long(buf + off + 4);
sizes[i] = get_le_long(buf + off);
types[i] = buf[off + 12];
if (types[i] != 0 || i == 2) {
offset = (u8)starts[i] * 512;
if (!min_offset_valid || offset < min_offset) {
min_offset = offset;
min_offset_valid = 1;
}
}
}
/* if min_offset_valid is still 0, the default of min_offset=0 is okay */
if (section->pos == min_offset) {
/* either min_offset is zero, or we were analyzing the whole disk */
base_offset = section->pos;
} else if (section->pos == 0) {
/* are we analyzing the slice alone? */
print_line(level + 1, "Adjusting offsets for disklabel in a DOS partition at sector %llu", min_offset >> 9);
base_offset = min_offset;
} else if (min_offset == 0) {
/* assume relative offsets after all */
base_offset = 0;
} else {
print_line(level + 1, "Warning: Unable to adjust offsets, your mileage may vary");
base_offset = section->pos;
}
/* loop over partitions: print and analyze */
did_recurse = 0;
for (i = 0; i < partcount; i++) {
pn = 'a' + i;
if (types[i] == 0 && i != 2)
continue;
sprintf(append, " from %lu", starts[i]);
format_blocky_size(s, sizes[i], 512, "sectors", append);
print_line(level, "Partition %c: %s",
pn, s);
print_line(level + 1, "Type %d (%s)",
types[i], get_name_for_bsdtype(types[i]));
if (types[i] == 0 || sizes[i] == 0)
continue;
offset = (u8)starts[i] * 512;
if (offset < base_offset) {
print_line(level + 1, "(Illegal start offset, no detection)");
} else if (offset == base_offset) {
print_line(level + 1, "Includes the disklabel and boot code");
/* recurse for content detection, but carefully */
analyze_recursive(section, level + 1,
offset - base_offset, (u8)sizes[i] * 512,
FLAG_IN_DISKLABEL);
did_recurse = 1;
} else {
/* recurse for content detection */
analyze_recursive(section, level + 1,
offset - base_offset, (u8)sizes[i] * 512,
0);
}
}
if (did_recurse)
stop_detect(); /* don't run other detectors; we already did that
for an overlapping partition. */
}
/*
* FreeBSD boot loader (?)
*/
void detect_bsd_loader(SECTION *section, int level)
{
unsigned char *buf;
if (section->flags & FLAG_IN_DISKLABEL)
return;
if (get_buffer(section, 0, 512, (void **)&buf) == 512) {
if (get_le_short(buf + 0x1b0) == 0xbb66) {
print_line(level, "FreeBSD boot manager (i386 boot0 at sector 0)");
} else if (get_le_long(buf + 0x1f6) == 0 &&
get_le_long(buf + 0x1fa) == 50000 &&
get_le_short(buf + 0x1fe) == 0xaa55) {
print_line(level, "FreeBSD boot loader (i386 boot1 at sector 0)");
}
}
if (get_buffer(section, 1024, 512, (void **)&buf) == 512) {
if (memcmp(buf + 2, "BTX", 3) == 0) {
print_line(level, "FreeBSD boot loader (i386 boot2/BTX %d.%02d at sector 2)",
(int)buf[5], (int)buf[6]);
}
}
}
/*
* Solaris SPARC disklabel
*/
void detect_solaris_disklabel(SECTION *section, int level)
{
unsigned char *buf;
int i, off1, off2, types[8], did_recurse;
u4 sizes[8];
u8 starts[8], cylsize, offset;
char s[256], append[256], pn;
if (section->flags & FLAG_IN_DISKLABEL)
return;
if (get_buffer(section, 0, 512, (void **)&buf) < 512)
return;
if (get_be_short(buf + 508) != 0xDABE)
return;
print_line(level, "Solaris SPARC disklabel");
cylsize = (u8)get_be_short(buf + 436) * (u8)get_be_short(buf + 438);
for (i = 0, off1 = 142, off2 = 444; i < 8; i++, off1 += 4, off2 += 8) {
types[i] = get_be_short(buf + off1);
starts[i] = get_be_long(buf + off2) * cylsize;
sizes[i] = get_be_long(buf + off2 + 4);
}
/* loop over partitions: print and analyze */
did_recurse = 0;
for (i = 0; i < 8; i++) {
pn = '0' + i;
if (sizes[i] == 0)
continue;
sprintf(append, " from %llu", starts[i]);
format_blocky_size(s, sizes[i], 512, "sectors", append);
print_line(level, "Partition %c: %s", pn, s);
print_line(level + 1, "Type %d",
types[i]);
offset = starts[i] * 512;
if (offset == 0) {
print_line(level + 1, "Includes the disklabel");
/* recurse for content detection, but carefully */
analyze_recursive(section, level + 1,
offset, (u8)sizes[i] * 512,
FLAG_IN_DISKLABEL);
did_recurse = 1;
} else {
/* recurse for content detection */
analyze_recursive(section, level + 1,
offset, (u8)sizes[i] * 512,
0);
}
}
if (did_recurse)
stop_detect(); /* don't run other detectors; we already did that
for the first partition, which overlaps with
the disklabel itself. */
}
/*
* Solaris x86 vtoc
*/
static char * vtoctype_names[] = {
"Unused",
"Boot",
"Root",
"Swap",
"Usr",
"Overlap",
"Stand",
"Var",
"Home",
"Alternate sector",
"Cache"
};
static char * get_name_for_vtoctype(int type)
{
if (type >= 0 && type <= 10)
return vtoctype_names[type];
return "Unknown";
}
void detect_solaris_vtoc(SECTION *section, int level)
{
unsigned char *buf;
int i, off, partcount, sectorsize, types[16], did_recurse;
u4 starts[16], sizes[16];
u4 version;
u8 offset;
char s[256], append[64];
if (section->flags & FLAG_IN_DISKLABEL)
return;
if (get_buffer(section, 512, 512, (void **)&buf) < 512)
return;
if (get_le_long(buf + 12) != 0x600DDEEE)
return;
version = get_le_long(buf + 16);
if (version != 1) {
print_line(level, "Solaris x86 disklabel, unknown version %lu", version);
return;
}
partcount = get_le_short(buf + 30);
if (partcount > 16) {
print_line(level, "Solaris x86 disklabel, version 1, %d partitions (limiting to 16)",
partcount);
partcount = 16;
} else {
print_line(level, "Solaris x86 disklabel, version 1, %d partitions",
partcount);
}
sectorsize = get_le_short(buf + 28);
if (sectorsize != 512)
print_line(level + 1, "Unusual sector size %d bytes, your mileage may vary",
sectorsize);
get_string(buf + 20, 8, s);
if (s[0])
print_line(level + 1, "Volume name \"%s\"", s);
for (i = 0, off = 72; i < partcount; i++, off += 12) {
types[i] = get_le_short(buf + off);
starts[i] = get_le_long(buf + off + 4);
sizes[i] = get_le_long(buf + off + 8);
}
/* loop over partitions: print and analyze */
did_recurse = 0;
for (i = 0; i < partcount; i++) {
if (sizes[i] == 0)
continue;
sprintf(append, " from %lu", starts[i]);
format_blocky_size(s, sizes[i], 512, "sectors", append);
print_line(level, "Partition %d: %s",
i, s);
print_line(level + 1, "Type %d (%s)",
types[i], get_name_for_vtoctype(types[i]));
offset = (u8)starts[i] * 512;
if (offset == 0) {
print_line(level + 1, "Includes the disklabel");
/* recurse for content detection, but carefully */
analyze_recursive(section, level + 1,
offset, (u8)sizes[i] * 512,
FLAG_IN_DISKLABEL);
did_recurse = 1;
} else {
/* recurse for content detection */
analyze_recursive(section, level + 1,
offset, (u8)sizes[i] * 512,
0);
}
}
if (did_recurse)
stop_detect(); /* don't run other detectors; we already did that
for an overlapping partition. */
}
/*
* QNX4 file system
*/
void detect_qnx(SECTION *section, int level)
{
unsigned char *buf;
if (get_buffer(section, 512, 512, (void **)&buf) < 512)
return;
/* check signature */
if (get_le_long(buf) != 0x0000002f)
return;
/* NOTE: This is actually the string "/", the file name of the root
directory. QNX4 fs does not have a real superblock, just an
aggregate of 4 inodes for certain special files. */
/* tell the user */
print_line(level, "QNX4 file system");
}
/*
* Veritas VxFS
*/
void detect_vxfs(SECTION *section, int level)
{
unsigned char *buf;
int en, version;
u4 blocksize, blockcount;
char s[256];
if (get_buffer(section, 1024, 1024, (void **)&buf) < 1024)
return;
/* check signature */
for (en = 0; en < 2; en++) {
if (get_ve_long(en, buf) == 0xA501FCF5) {
version = get_ve_long(en, buf + 4);
print_line(level, "Veritas VxFS file system, version %d, %s",
version, get_ve_name(en));
blocksize = get_ve_long(en, buf + 32);
blockcount = get_ve_long(en, buf + 36);
format_blocky_size(s, blockcount, blocksize, "blocks", NULL);
print_line(level + 1, "Volume size %s", s);
}
}
}
/* EOF */
syntax highlighted by Code2HTML, v. 0.9.1