From: lm@slovax.Eng.Sun.COM (Larry McVoy)
Newsgroups: comp.unix.wizards
Subject: Re: file system layout
Date: 31 Dec 1992 22:47:35 GMT
Organization: Sun Microsystems, Inc.  Mt. View, Ca.
NNTP-Posting-Host: slovax

singer@ll.mit.edu (Matthew R. Singer) writes:
: If I were to pre-write a file a Sun disk, is there a simple way to find out
: what blocks on the disk the file system has allocated for it.  

Yup.  Source included below.  This is essentialy the read path of UFS. 
Pretty simple, eh?  I saw a perl script that did this once - now that's 
cool.

: Since I 
: am writing just one big file on a newly formatted disk, it should be 
: contiguous (right?).

Nope.  Even though I wacked it to do contig allocation when it can, it
still switches cylinder groups every meg or so.  I suppose you could
dick around with the tuning parameters to make one mondo cyl group.


If you want more info on the SunOS UFS, send mail like so

	Mail archives@slovax.eng.sun.com
	Subject: fs_cluster
	^D

That will send you the clustering paper.  (Yeah, I gotta get netlib.)

--lm

Here's the code.  


#include <stdio.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/vnode.h>
#ifdef	svr4
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_inode.h>
#define	DEV
#else
#include <ufs/fs.h>
#include <ufs/inode.h>
#include <sys/dir.h>
#endif
#include <sys/stat.h>

#define SECTORSIZE	512
#define SUPERADDR	(16*SECTORSIZE)

union {
	char	sb_buffer[SBSIZE];
	struct	fs fs;
} sb;
union {
	char	i_buffer[MAXBSIZE];
	struct dinode dinode[MAXBSIZE/sizeof(struct dinode)];
} di;
char	*cmdname;
void	*malloc();
int	block;	/* global because I'm too lazy to pass a pointer */

main(argc, argv)
	int	argc;
	char	*argv[];
{
	struct	stat	statb;
	struct	fs	*fsp;
	struct	dinode	*ip;
	char	*file;
	int	*blk;
	int	size;
	int	devfd;
	int	inode;
	int	offset;
	int	seekaddr;
	int	i, j;
	int	extent;

	cmdname = argv[0];
	if (argc != 2 && argc != 3) {
		fprintf(stderr, "Usage: %s file [diskdev]\n", cmdname);
		exit(1);
	}
	file = argv[1];
	if (stat(file, &statb) == -1) {
		fprintf(stderr, "File %s not found\n", file);
		exit(1);
	}
	sync();
	inode = statb.st_ino;
	if (argc == 3) {
		devfd = open(argv[2], 0);
#ifndef	DEV
	} else {
		devfd = getdev(statb.st_dev);
#endif
	}
	if (lseek(devfd, SUPERADDR, 0) == -1) {
		fprintf(stderr, "Seek to superblock failed\n");
		exit(1);
	}
	fsp = &sb.fs;
	if (read(devfd, sb.sb_buffer, SBSIZE) == -1) {
		fprintf(stderr, "Read of superblock failed\n");
		exit(1);
	}
	if (fsp->fs_magic != FS_MAGIC) {
		fprintf(stderr, "Not a superblock\n");
		exit(1);
	}
	block = itod(fsp, inode);
	offset = itoo(fsp, inode);
	seekaddr = fsbtodb(fsp, block) * SECTORSIZE;
	if (lseek(devfd, seekaddr, 0) == -1) {
		fprintf(stderr, "Seek to block %d offset %d failed\n", 
			block, offset);
		exit(1);
	}
	if (read(devfd, di.i_buffer, fsp->fs_bsize) == -1) {
		fprintf(stderr, "Read of inode %d failed\n", inode);
		exit(1);
	}
	ip = &di.dinode[offset];
	/* fprintf(stderr, "ncg %d rotor %d\n", fsp->fs_ncg, fsp->fs_cgrotor);*/
	/* printf("%d\n", fsp->fs_fpg); */
	size = ip->di_size;
	for (i = 0; i < NDADDR && size > 0; ) {
		extent = findextent(fsp, &ip->di_db[i], NDADDR - i);
		printf("off=%dK block=%d extent=%dK\n", 
		    i * fsp->fs_bsize / 1024, ip->di_db[i],
		    extent * fsp->fs_bsize / 1024);
		i += extent ? extent : 1;
		if (i * fsp->fs_bsize >= size)
		    exit(0);
	}
	block = NDADDR;
	blk = (int *) malloc(fsp->fs_bsize);
	/*
	 * block of pointers
	 */
	doblk(devfd, fsbtodb(fsp, ip->di_ib[0]) * SECTORSIZE, fsp, size);
	/*
	 * Block of pointers to pointers
	 */
	if (lseek(devfd, fsbtodb(fsp, ip->di_ib[1]) * SECTORSIZE, 0) == -1) {
		fprintf(stderr, "Seek to indirect block %d failed\n",
			fsbtodb(fsp, ip->di_ib[1]));
		exit(1);
	}
	if (read(devfd, (char *) blk, fsp->fs_bsize) != fsp->fs_bsize) {
		fprintf(stderr, "Read of indirect block %d failed\n",
			fsbtodb(fsp, ip->di_ib[1]));
		exit(1);
	}
	for (j = 0; j < NINDIR(fsp); j++) {
		doblk(devfd, fsbtodb(fsp, blk[j]) * SECTORSIZE, fsp, size);
	}
	/*
	 * Too lazy to do this
	 */
	printf("Quitting early\n");
	exit(0);
	/*NOTREACHED*/
}

doblk(fd, loc, fsp, size)
	struct	fs	*fsp;
{
	int	*blk;
	int	j;
	int	extent;

	if (lseek(fd, loc, 0) != loc) {
		perror("lseek");
		exit(0);
	}
	blk = (int*)malloc(fsp->fs_bsize);
	if (read(fd, (char*)blk, fsp->fs_bsize) != fsp->fs_bsize) {
		perror("read");
		exit(0);
	}
	for (j = 0; j < NINDIR(fsp); ) {
		if (extent = findextent(fsp, &blk[j], NINDIR(fsp) - j)) {
			printf("off=%dK block=%d extent=%dK\n",
			    block * fsp->fs_bsize / 1024, 
			    blk[j], extent * fsp->fs_bsize / 1024);
			block += extent;
			j += extent;
		} else {
			printf("off=%dK HOLE\n", block * fsp->fs_bsize / 1024);
			block += 1;
			j += 1;
		}
		if (block * fsp->fs_bsize >= size)
		    exit(0);
	}
}

#ifndef	DEV
getdev(dev)
	dev_t dev;
{
	DIR *dirp;
	struct direct *dp;
	struct stat statb;
	char buf[100];
	int fd;

	dirp = opendir("/dev");
	if (dirp == NULL) {
		fprintf(stderr, "cannot open /dev\n");
		exit(1);
	}
	while (dp = readdir(dirp)) {
		if (dp->d_fileno == 0) {
			continue;
		}
		sprintf(buf, "/dev/%s", dp->d_name);
		if (stat(buf, &statb) == -1) {
			continue;
		}
		if (statb.st_rdev == dev && (statb.st_mode & IFMT) == IFBLK) {
			sprintf(buf, "/dev/r%s", dp->d_name);
			fd = open(buf, 0);
			if (fd < 0) {
				fprintf(stderr, "cannot open %s\n", buf);
				exit(1);
			}
			closedir(dirp);
			return (fd);
		}
	}
	fprintf(stderr, "Device %x not found (NFS file?).\n", dev & 0xffff);
	exit(1);
	/*NOTREACHED*/
}
#endif

static int
findextent(fs, sbp, n)
	struct fs *fs;
	daddr_t *sbp;
	register int n;
{
	register daddr_t bn, nextbn;
	register daddr_t *bp;
	register int diff;

	if (n <= 0)
		return (0);
	bn = *sbp;
	if (bn == 0)
		return (0);
	diff = fs->fs_frag;
	bp = sbp;
	while (--n > 0) {
		nextbn = *(bp + 1);
		if (nextbn == 0 || bn + diff != nextbn)
			break;
		bn = nextbn;
		bp++;
	}
	return ((int)(bp - sbp) + 1);
}
---
Larry McVoy			(415) 336-7627			 lm@sun.com
Newsgroups: comp.unix.wizards