/* Program to seek forward and write a single byte, then stat the file.
   This lets us probe to find out how the file is structured.

   If you seek forward by N and write 1 byte, the file will have length
   N+1.  Therefore, this code seeks forward by N-1 and writes 1 byte, so
   that the file size matches the argument to oneByteFile.

   With this definition, the last byte of any block requires a seek
   position that is a multiple of 4096 (the device block size on Puma).

Scott D. Anderson
scott.anderson@acm.org
Spring 2005
*/

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>		/* for exit() */
#include <sys/types.h>		/* for open() */
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>		/* for sprintf */

#define BSD64
#define __USE_LARGEFILE64
#define __USE_FILE_OFFSET64

/* The following is initialized in main() */

char buf[1];

void oneByteFile(off_t position)
{
	char buf[1];
	struct stat statbuf;
	char filename[100];
	char error_message[100];
	int fd;
    
	sprintf(filename, "file%10.10ld.b", position);
	fd = open(filename,
		  O_WRONLY | O_CREAT | O_TRUNC,
		  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
	if ( -1 == fd ) {
		sprintf(error_message, "opening %s", filename);
		perror(error_message);
		return;
	}
	/* Note the -1, so that we write to the next position, which will then
	   be the one we want. */
	if ( -1 == lseek64(fd, position-1, SEEK_SET) ) perror("seeking");
    
	if ( -1 == write(fd,buf,1)) perror("writing");
	if ( 0 != fstat(fd,&statbuf) ) perror("fstatting");
	printf("%10ld => %10ld\n",
	       position,
	       (statbuf.st_blocks*512) / statbuf.st_blksize);
	if (-1 == close(fd)) perror("closing");
}

int main( int argc, char* argv[] )
{
	long long int blk = 4096;
	int i;
	long long int pos1 = 12 * blk + 1; /* first byte of first
					      indirect block */
	long long int pos2 = blk * (12 + (blk / 4)); /* last byte of last
							indirect block */
	long long int pos3 = pos2 + blk * (blk / 4) * (blk / 4); 	    
	long long int pos4 = pos3 + blk * (blk / 4) * (blk / 4) * (blk / 4); 

	printf("int %d long %d long long %d off_t %d\n",
	       sizeof(int), sizeof(long), sizeof(long long int), sizeof(off_t));
	printf("%12lld %12lld %12lld %12lld\n",pos1, pos2, pos3, pos4);
	printf("max long = %ld, max long long int = %lld\n",
	       (long) 0x7FFFFFFF,
	       (long long) 0x7FFFFFFFFFFFFFFF);
	buf[0] = 'A';
	for (i = 0; i < 12; i++) {
		printf("first byte of block %d \t", i);
		oneByteFile(i * blk + 1);
		printf(" last byte of block %d \t", i);
		oneByteFile((i + 1) * blk);
	}
	printf("first byte of first indirect block\t");
	oneByteFile(pos1);
	printf(" last byte of last  indirect block\t");
	oneByteFile(pos2);
	printf("first byte of first double-indirect block\t");
	oneByteFile(pos2 + 1);
	printf("biggest seekable file");
	oneByteFile(0x7FFFFFFF);
	/*
	  the following are impossible, since lseek only takes a long

	  printf(" last byte of  last double-indirect block\t");
	  oneByteFile(pos3);
	  printf("first byte of first triple-indirect block\t");
	  oneByteFile(pos3+1);
	  printf(" last byte of  last triple-indirect block\t");
	  oneByteFile(pos4);
	*/
    
	return 0;
}
