Page MenuHomeClusterLabs Projects

layout.c
No OneTemporary

layout.c

/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
** Copyright (C) 2004 Red Hat, Inc. All rights reserved.
**
** This copyrighted material is made available to anyone wishing to use,
** modify, copy, or redistribute it subject to the terms and conditions
** of the GNU General Public License v.2.
**
*******************************************************************************
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <errno.h>
#define __user
#include <linux/gfs2_ioctl.h>
#include <linux/gfs2_ondisk.h>
#include "osi_list.h"
#include "linux_endian.h"
#include "gfs2_tool.h"
#define LAYOUT_DATA_QUANTUM (4194304)
struct extent {
osi_list_t list;
uint64_t offset;
uint64_t start;
unsigned int len;
};
typedef struct extent extent_t;
struct buffer {
osi_list_t list;
uint64_t blkno;
char *data;
int touched;
};
typedef struct buffer buffer_t;
struct world {
char *buf_data;
unsigned int buf_size;
int buf_count;
osi_list_t blist;
osi_list_t elist;
struct gfs2_sb sb;
unsigned int diptrs;
unsigned int inptrs;
unsigned int jbsize;
unsigned int hash_bsize;
unsigned int hash_ptrs;
buffer_t *dibh;
struct gfs2_dinode di;
};
typedef struct world world_t;
typedef void (*pointer_call_t) (world_t *w,
unsigned int height, uint64_t bn, void *data);
typedef void (*leaf_call_t) (world_t *w,
uint32_t index, uint32_t len, uint64_t leaf_no,
void *data);
/**
* build_list - build a list of buffer_t's to represent the data from the kernel
* @w: the world structure
*
*/
static void
build_list(world_t *w)
{
buffer_t *b;
unsigned int x;
for (x = 0; x < w->buf_count; x += sizeof(uint64_t) + w->sb.sb_bsize) {
b = malloc(sizeof(buffer_t));
if (!b)
die("out of memory\n");
memset(b, 0, sizeof(buffer_t));
b->blkno = *(uint64_t *) (w->buf_data + x);
b->data = w->buf_data + x + sizeof(uint64_t);
osi_list_add_prev(&b->list, &w->blist);
}
if (x != w->buf_count)
die("the kernel passed back unaligned data\n");
}
/**
* check_list - check the buffers passed back by the kernel
* @w: the world
*
*/
static void
check_list(world_t *w)
{
osi_list_t *tmp;
buffer_t *b;
struct gfs2_meta_header mh;
char *type;
for (tmp = w->blist.next; tmp != &w->blist; tmp = tmp->next) {
b = osi_list_entry(tmp, buffer_t, list);
gfs2_meta_header_in(&mh, b->data);
if (mh.mh_magic != GFS2_MAGIC)
die("bad magic number on block\n");
switch (mh.mh_type) {
case GFS2_METATYPE_DI:
type = "GFS2_METATYPE_DI";
if (w->dibh)
die("more than one dinode in file\n");
else {
w->dibh = b;
gfs2_dinode_in(&w->di, b->data);
b->touched = TRUE;
}
break;
case GFS2_METATYPE_IN:
type = "GFS2_METATYPE_IN";
break;
case GFS2_METATYPE_LF:
type = "GFS2_METATYPE_LF";
break;
case GFS2_METATYPE_JD:
type = "GFS2_METATYPE_JD";
break;
case GFS2_METATYPE_EA:
type = "GFS2_METATYPE_EA";
break;
case GFS2_METATYPE_ED:
die("GFS2_METATYPE_ED shouldn't be present\n");
default:
die("strange meta type\n");
}
}
if (!w->dibh)
die("no dinode\n");
}
/**
* getbuf - get the buffer_t for a given block number
* @w: the world
* @blkno: the block number
*
* Returns: the buffer_t
*/
static buffer_t *
getbuf(world_t *w, uint64_t blkno)
{
osi_list_t *tmp;
buffer_t *b;
for (tmp = w->blist.next; tmp != &w->blist; tmp = tmp->next) {
b = osi_list_entry(tmp, buffer_t, list);
if (b->blkno == blkno) {
osi_list_del(&b->list);
osi_list_add(&b->list, &w->blist);
b->touched = TRUE;
return b;
}
}
die("buffer not found\n");
}
/**
* recursive_scan - call a function for each block pointer in a file
* @w: the world
* @height: the height of the block being pointed to
* @block: the block being pointed to
* @pc: the function to call
* @data: private data for the @pc function
*
*/
static void
recursive_scan(world_t *w,
unsigned int height, uint64_t block, pointer_call_t pc, void *data)
{
buffer_t *b = NULL;
uint64_t *top, *bottom;
uint64_t bn;
if (!height) {
b = w->dibh;
top = (uint64_t *) (b->data + sizeof(struct gfs2_dinode));
bottom =
(uint64_t *) (b->data + sizeof(struct gfs2_dinode)) +
w->diptrs;
} else {
b = getbuf(w, block);
top = (uint64_t *) (b->data + sizeof(struct gfs2_meta_header));
bottom =
(uint64_t *) (b->data + sizeof(struct gfs2_meta_header)) +
w->inptrs;
}
for (; top < bottom; top++) {
bn = gfs2_64_to_cpu(*top);
pc(w, height, bn, data);
if (bn && height < w->di.di_height - 1)
recursive_scan(w, height + 1, bn, pc, data);
}
}
/**
* bmap - return the buffer_t for a given logical block in the file
* @w: the world
* @lbn: the logical block number
*
* Returns: the buffer_t
*/
static buffer_t *
bmap(world_t *w, uint64_t lbn)
{
osi_list_t *tmp;
extent_t *e;
for (tmp = w->elist.next; tmp != &w->elist; tmp = tmp->next) {
e = osi_list_entry(tmp, extent_t, list);
if (e->offset <= lbn && lbn < e->offset + e->len)
return getbuf(w, e->start + lbn - e->offset);
}
return NULL;
}
/**
* journaled_read - read some of the contents of a journaled file
* @w: the world
* @buf: the buffer to read into
* @offset: the offset to read from
* @size: the number of bytes to read
*
*/
static void
journaled_read(world_t *w, char *buf, uint64_t offset, unsigned int size)
{
buffer_t *b;
uint64_t lbn;
unsigned int o, chunk;
if (!(w->di.di_flags & GFS2_DIF_JDATA))
die("not a journaled file\n");
if (!w->di.di_height) {
if (offset >= w->sb.sb_bsize - sizeof(struct gfs2_dinode))
memset(buf, 0, size);
else {
chunk =
w->sb.sb_bsize - sizeof(struct gfs2_dinode) -
offset;
if (chunk > size)
chunk = size;
memcpy(buf,
w->dibh->data + sizeof(struct gfs2_dinode) +
offset, chunk);
if (chunk < size)
memset(buf + chunk, 0, size - chunk);
}
} else
while (size) {
lbn = offset / w->jbsize;
o = offset % w->jbsize;
chunk = (size > w->jbsize - o) ? (w->jbsize - o) : size;
b = bmap(w, lbn);
if (b)
memcpy(buf,
b->data +
sizeof(struct gfs2_meta_header) + o,
chunk);
else
memset(buf, 0, chunk);
buf += chunk;
offset += chunk;
size -= chunk;
}
}
/**
* foreach_leaf - call a function for each leaf in a directory
* @w: the world
* @lc: the function to call for each each
* @data: private data to pass to it
*
* Returns: 0 on success, -EXXX on failure
*/
static void
foreach_leaf(world_t *w, leaf_call_t lc, void *data)
{
buffer_t *b;
struct gfs2_leaf leaf;
uint32_t hsize, len;
uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
uint32_t index = 0;
uint64_t lp[w->hash_ptrs];
uint64_t leaf_no;
hsize = 1 << w->di.di_depth;
if (hsize * sizeof(uint64_t) != w->di.di_size)
die("bad hash table size\n");
while (index < hsize) {
lp_offset = index % w->hash_ptrs;
ht_offset = index - lp_offset;
if (ht_offset_cur != ht_offset) {
journaled_read(w, (char *) lp,
ht_offset * sizeof(uint64_t),
w->hash_bsize);
ht_offset_cur = ht_offset;
}
leaf_no = gfs2_64_to_cpu(lp[lp_offset]);
if (!leaf_no)
die("NULL leaf pointer\n");
b = getbuf(w, leaf_no);
gfs2_leaf_in(&leaf, b->data);
len = 1 << (w->di.di_depth - leaf.lf_depth);
lc(w, index, len, leaf_no, data);
index += len;
}
if (index != hsize)
die("screwed up directory\n");
}
/**
* add_extent - add an extend to the list of the file's data extents
* @w: the world
* @offset: the starting logical block of the extent
* @start: the starting disk block of the extent
* @len: the number of blocks in the extent
*
*/
static void
add_extent(world_t *w, uint64_t offset, uint64_t start, unsigned int len)
{
extent_t *e;
e = malloc(sizeof(extent_t));
if (!e)
die("out of memory\n");
memset(e, 0, sizeof(extent_t));
e->offset = offset;
e->start = start;
e->len = len;
osi_list_add_prev(&e->list, &w->elist);
}
struct do_pf_s {
unsigned int height;
uint64_t offset;
uint64_t start;
uint64_t skip;
unsigned int len;
};
typedef struct do_pf_s do_pf_t;
/**
* do_pf: called for every pointer in the file (prints/collects extent info)
* @w: the world
* @height: the height of the block containing the pointer
* @bn: the contents of the pointer
* @data: a do_pf_t structure
*
*/
static void
do_pf(world_t *w, unsigned int height, uint64_t bn, void *data)
{
do_pf_t *pf = (do_pf_t *) data;
unsigned int x;
uint64_t skip;
if (pf->height < height + 1)
return;
if (!bn) {
if (pf->height == height + 1)
pf->skip++;
else {
x = pf->height - height - 1;
skip = w->inptrs;
while (--x)
skip *= w->inptrs;
pf->skip += skip;
}
return;
}
if (pf->height == height + 1) {
if (pf->start + pf->len == bn && pf->len == pf->skip) {
pf->len++;
pf->skip++;
} else {
if (pf->start) {
printf(" %-20" PRIu64 " %-20" PRIu64 " %-20"
PRIu64 " %u\n", pf->offset,
pf->offset + pf->len - 1, pf->start,
pf->len);
if (pf->height == w->di.di_height)
add_extent(w, pf->offset, pf->start,
pf->len);
}
pf->offset += pf->skip;
pf->start = bn;
pf->len = 1;
pf->skip = 1;
}
}
}
/**
* print_file - print out the extent lists for all the heights of a file
* @w: the world
*
*/
static void
print_file(world_t *w)
{
do_pf_t pf;
unsigned int h;
char *type;
switch (w->di.di_mode & S_IFMT) {
case 0:
type = "Unknown";
break;
case S_IFREG:
type = "File";
break;
case S_IFDIR:
type = "Directory";
break;
case S_IFLNK:
type = "Symbolic Link";
break;
case S_IFBLK:
type = "Block Device";
break;
case S_IFCHR:
type = "Character Device";
break;
case S_IFIFO:
type = "FIFO";
break;
case S_IFSOCK:
type = "Socket";
break;
default:
die("strange file type\n");
};
printf("%s dinode:\n", type);
printf(" %" PRIu64 "\n", w->di.di_num.no_addr);
if (!w->di.di_height) {
if (S_ISDIR(w->di.di_mode)) {
if (w->di.di_flags & GFS2_DIF_EXHASH)
printf("\nStuffed hash table\n");
} else
printf("\nStuffed file data\n");
return;
}
for (h = 1; h <= w->di.di_height; h++) {
if (S_ISDIR(w->di.di_mode))
type =
(h == w->di.di_height) ? "hash table" : "indirect";
else
type = (h == w->di.di_height) ? "data" : "indirect";
printf("\n");
printf("At height %u (%s):\n", h, type);
printf(" %-20s %-20s %-20s %s\n",
"From LBlock", "To LBlock", "DBlock", "Blocks");
memset(&pf, 0, sizeof(do_pf_t));
pf.height = h;
recursive_scan(w, 0, 0, do_pf, &pf);
if (pf.start) {
printf(" %-20" PRIu64 " %-20" PRIu64 " %-20" PRIu64
" %u\n", pf.offset, pf.offset + pf.len - 1,
pf.start, pf.len);
if (h == w->di.di_height)
add_extent(w, pf.offset, pf.start, pf.len);
}
}
}
/**
* do_lc - print out info about a leaf block
* @w: the world
* @index: the index of the leaf
* @len: the number of pointers to the leaf
* @leaf_no: the leaf block number
* @data: unused
*
*/
static void
do_lc(world_t *w, uint32_t index, uint32_t len, uint64_t leaf_no, void *data)
{
buffer_t *b;
struct gfs2_leaf leaf;
uint64_t blk;
for (blk = leaf_no; blk; blk = leaf.lf_next) {
b = getbuf(w, blk);
gfs2_leaf_in(&leaf, b->data);
printf(" %.8X %.8X %-20" PRIu64
" %u\n", index << (32 - w->di.di_depth),
((index + len) << (32 - w->di.di_depth)) - 1, blk,
leaf.lf_entries);
}
}
/**
* print_leaves - print out the location of the exhash leaves
* @w: the world
*
*/
static void
print_leaves(world_t *w)
{
printf("\n");
if (w->di.di_flags & GFS2_DIF_EXHASH) {
printf("Leaves:\n");
printf(" %-20s %-20s %-20s %s\n",
"From Hash", "To Hash", "DBlock", "Entries");
foreach_leaf(w, do_lc, NULL);
} else
printf("Stuffed directory data\n");
}
/**
* print_eattr_data - print out the locations of the eattr data blocks
* @w: the world
*
*/
#define MAKE_MULT8(x) (((x) + 7) & ~7)
#define GFS2_EA_REC_LEN(ea) gfs2_32_to_cpu((ea)->ea_rec_len)
#define GFS2_EA_IS_STUFFED(ea) (!(ea)->ea_num_ptrs)
#define GFS2_EA_IS_LAST(ea) ((ea)->ea_flags & GFS2_EAFLAG_LAST)
#define GFS2_EA2NAME(ea) ((char *)((struct gfs2_ea_header *)(ea) + 1))
#define GFS2_EA2DATAPTRS(ea) \
((uint64_t *)(GFS2_EA2NAME(ea) + MAKE_MULT8((ea)->ea_name_len)))
#define GFS2_EA2NEXT(ea) \
((struct gfs2_ea_header *)((char *)(ea) + GFS2_EA_REC_LEN(ea)))
#define GFS2_EA_BH2FIRST(b) \
((struct gfs2_ea_header *)((b)->data + \
sizeof(struct gfs2_meta_header)))
static void
print_eattr_data(world_t *w, uint64_t blkno, int *first)
{
buffer_t *b = getbuf(w, blkno);
struct gfs2_ea_header *ea;
ea = GFS2_EA_BH2FIRST(b);
for (;;) {
if (!GFS2_EA_IS_STUFFED(ea)) {
char name[300];
uint64_t *p, blkno;
uint64_t b;
unsigned int l;
unsigned int x;
int c;
if (*first) {
printf("\nExtended Attributes data blocks:\n");
printf(" %-20s %-10s %s\n",
"DBlock", "Blocks", "Name");
*first = FALSE;
}
if (ea->ea_type == GFS2_EATYPE_UNUSED)
strcpy(name, "unused");
else {
unsigned int x;
switch (ea->ea_type) {
case GFS2_EATYPE_USR:
strcpy(name, "user.");
break;
case GFS2_EATYPE_SYS:
strcpy(name, "system.");
break;
default:
strcpy(name, "unknown.");
break;
}
x = strlen(name);
memcpy(name + x,
GFS2_EA2NAME(ea), ea->ea_name_len);
name[x + ea->ea_name_len] = 0;
}
b = 0;
l = 0;
c = FALSE;
p = GFS2_EA2DATAPTRS(ea);
for (x = 0; x < ea->ea_num_ptrs; x++) {
blkno = gfs2_64_to_cpu(*p);
if (b + l == blkno)
l++;
else {
if (b) {
printf(" %-20" PRIu64
" %-10u %s\n", b, l,
name);
if (!c) {
strcat(name, " (cont)");
c = TRUE;
}
}
b = blkno;
l = 1;
}
p++;
}
printf(" %-20" PRIu64 " %-10u %s\n", b, l, name);
}
if (GFS2_EA_IS_LAST(ea))
break;
ea = GFS2_EA2NEXT(ea);
}
}
/**
* print_eattr - print out the locations of the eattr blocks
* @w: the world
*
*/
static void
print_eattr(world_t *w)
{
int first = TRUE;
if (w->di.di_flags & GFS2_DIF_EA_INDIRECT) {
buffer_t *b = getbuf(w, w->di.di_eattr);
uint64_t *blkno;
unsigned int x;
printf("\nExtended Attribute indirect block:\n");
printf(" %" PRIu64 "\n", w->di.di_eattr);
printf("\nExtended Attribute blocks:\n");
blkno = (uint64_t *) (b->data + sizeof(struct gfs2_meta_header));
for (x = 0; x < w->inptrs; x++) {
if (!*blkno)
break;
printf(" %" PRIu64 "\n", gfs2_64_to_cpu(*blkno));
blkno++;
}
blkno = (uint64_t *) (b->data + sizeof(struct gfs2_meta_header));
for (x = 0; x < w->inptrs; x++) {
if (!*blkno)
break;
print_eattr_data(w, gfs2_64_to_cpu(*blkno), &first);
blkno++;
}
} else {
printf("\nExtended Attribute block:\n");
printf(" %" PRIu64 "\n", w->di.di_eattr);
print_eattr_data(w, w->di.di_eattr, &first);
}
}
/**
* check_for_untouched_buffers -
* @w: the world
*
*/
static void
check_for_untouched_buffers(world_t *w)
{
osi_list_t *tmp;
buffer_t *b;
for (tmp = w->blist.next; tmp != &w->blist; tmp = tmp->next) {
b = osi_list_entry(tmp, buffer_t, list);
if (b->touched)
continue;
printf("Buffer %" PRIu64 " untouched\n", b->blkno);
}
}
/**
* print_layout - print out the ondisk layout of a file
* @argc:
* @argv:
*
*/
void
print_layout(int argc, char **argv)
{
world_t w;
char *path;
int fd;
int retry = TRUE;
struct gfs2_ioctl gi;
int error;
memset(&w, 0, sizeof(world_t));
w.buf_size = LAYOUT_DATA_QUANTUM;
osi_list_init(&w.blist);
osi_list_init(&w.elist);
if (optind == argc)
die("Usage: gfs2_tool layout <filename> [buffersize]\n");
path = argv[optind++];
if (optind < argc ) {
w.buf_size = atoi(argv[3]);
retry = FALSE;
}
fd = open(path, O_RDONLY);
if (fd < 0)
die("can't open %s: %s\n", path, strerror(errno));
check_for_gfs2(fd, path);
{
char *argv[] = { "get_super" };
gi.gi_argc = 1;
gi.gi_argv = argv;
gi.gi_data = (char *)&w.sb;
gi.gi_size = sizeof(struct gfs2_sb);
error = ioctl(fd, GFS2_IOCTL_SUPER, &gi);
if (error != gi.gi_size)
die("error doing get_super (%d): %s\n",
error, strerror(errno));
}
w.diptrs = (w.sb.sb_bsize - sizeof(struct gfs2_dinode)) /
sizeof(uint64_t);
w.inptrs = (w.sb.sb_bsize - sizeof(struct gfs2_meta_header)) /
sizeof(uint64_t);
w.jbsize = w.sb.sb_bsize - sizeof(struct gfs2_meta_header);
w.hash_bsize = w.sb.sb_bsize / 2;
w.hash_ptrs = w.hash_bsize / sizeof(uint64_t);
for (;;) {
char *argv[] = { "get_file_meta" };
w.buf_data = malloc(w.buf_size);
if (!w.buf_data)
die("out of memory\n");
gi.gi_argc = 1;
gi.gi_argv = argv;
gi.gi_data = w.buf_data;
gi.gi_size = w.buf_size;
w.buf_count = ioctl(fd, GFS2_IOCTL_SUPER, &gi);
if (w.buf_count >= 0)
break;
if (errno == ENOMEM) {
if (retry) {
free(w.buf_data);
w.buf_size += LAYOUT_DATA_QUANTUM;
continue;
} else
die("%u bytes isn't enough memory\n",
w.buf_size);
}
die("error doing get_file_meta: %s\n",
strerror(errno));
}
build_list(&w);
check_list(&w);
print_file(&w);
if (S_ISDIR(w.di.di_mode))
print_leaves(&w);
if (w.di.di_eattr)
print_eattr(&w);
check_for_untouched_buffers(&w);
close(fd);
}

File Metadata

Mime Type
text/x-c
Expires
Wed, Feb 26, 12:28 AM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1457327
Default Alt Text
layout.c (17 KB)

Event Timeline