filestat.c

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>


/*
 * should be defined in header files but . . . 
 */
#define S_ISSOCK(x)	((x)&014)
ssize_t readlink(const char *, char *, size_t);
int lstat(const char *, struct stat *);

/*
 * printing permissions
 */
/* how many bits to shift left to get the set needed */
#define PRIV	9	/* privilege bits */
#define USER	6	/* permissions for UID */
#define GROUP	3	/* permissions for GID */
#define OTHER	0	/* permissions for everyone else */
char *p_rights[] = { "setuid", "setgid", "sticky" };
char *f_rights[] = { "read", "write", "execute" };
char *d_rights[] = { "list", "modify", "search" };
char perm_buf[128];	/* big enough to hold response */

/*
 * print permissions in a readable fashion
 * note what we print depends on the file type
 */
char *perm(int what, mode_t f_perms)
{
	char **rights;
	unsigned short bits;

	if (S_ISDIR(f_perms))
		rights = d_rights;
	else
		rights = f_rights;

	switch(what){
	case USER: case GROUP: case OTHER:
		bits = (f_perms>>what)&07;
		break;
	default:
		return("*** internal error ***");
	}
	perm_buf[0] = '\0';
	switch(bits){
	case 7:		/* read, write, and execute */
		(void) strcpy(perm_buf, rights[0]);
		(void) strcat(perm_buf, ", ");
		(void) strcat(perm_buf, rights[1]);
		(void) strcat(perm_buf, ", and ");
		(void) strcat(perm_buf, rights[2]);
		break;
	case 6:		/* read and write */
		(void) strcpy(perm_buf, rights[0]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, rights[1]);
		break;
	case 5:		/* read and execute */
		(void) strcpy(perm_buf, rights[0]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, rights[2]);
		break;
	case 4:		/* read */
		(void) strcpy(perm_buf, rights[0]);
		break;
	case 3:		/* write and execute */
		(void) strcpy(perm_buf, rights[1]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, rights[2]);
		break;
	case 2:		/* write */
		(void) strcpy(perm_buf, rights[1]);
		break;
	case 1:		/* execute */
		(void) strcpy(perm_buf, rights[2]);
		break;
	case 0:		/* execute */
		(void) strcpy(perm_buf, "do nothing");
		break;
	default:
		(void) strcpy(perm_buf, "*** internal error ***");
		break;
	}
	return(perm_buf);
}

/*
 * print privileges
 */
char *priv(mode_t p_perms)
{
	unsigned short bits;

	bits = (p_perms>>PRIV)&07;

	perm_buf[0] = '\0';

	switch(bits){
	case 7:		/* setuid, setgid, and sticky */
		(void) strcpy(perm_buf, p_rights[0]);
		(void) strcat(perm_buf, ", ");
		(void) strcat(perm_buf, p_rights[1]);
		(void) strcat(perm_buf, ", and ");
		(void) strcat(perm_buf, p_rights[2]);
		break;
	case 6:		/* setuid and setgid */
		(void) strcpy(perm_buf, p_rights[0]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, p_rights[1]);
		break;
	case 5:		/* setuid and sticky */
		(void) strcpy(perm_buf, p_rights[0]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, p_rights[2]);
		break;
	case 4:		/* setuid */
		(void) strcpy(perm_buf, p_rights[0]);
		break;
	case 3:		/* setgid and sticky */
		(void) strcpy(perm_buf, p_rights[1]);
		(void) strcat(perm_buf, " and ");
		(void) strcat(perm_buf, p_rights[2]);
		break;
	case 2:		/* setgid */
		(void) strcpy(perm_buf, p_rights[1]);
		break;
	case 1:		/* sticky */
		(void) strcpy(perm_buf, p_rights[2]);
		break;
	case 0:
		(void) strcpy(perm_buf, "none");
		break;
	default:
		(void) strcpy(perm_buf, "*** internal error ***");
		break;
	}
	return(perm_buf);
}

/*
 * this prints the file type
 */
char *ftype(mode_t f_perms)
{
	if (S_ISREG(f_perms))		return("regular file");
	else if (S_ISDIR(f_perms))	return("directory");
	else if (S_ISCHR(f_perms))	return("character special device");
	else if (S_ISBLK(f_perms))	return("block special device");
	else if (S_ISFIFO(f_perms))	return("fifo (named pipe)");
	else if (S_ISLNK(f_perms))	return("symbolic link");
	else if (S_ISSOCK(f_perms))	return("socket");
	/* should never get here, but just in case . . . */
	sprintf(perm_buf, "*** unknown type (%0o) ***", (f_perms>>12)&0xf);
	return(perm_buf);
}

/*
 * print time
 */
char p_time[1024];
char *timefmt = "%A, %B %e, %Y at %l:%M:%S%p %Z";

char *prtime(time_t tock)
{
	struct tm ticktock;

	ticktock = *localtime(&tock);
	if (!strftime(p_time, sizeof(p_time), timefmt, &ticktock))
		sprintf(p_time, "%ld", tock);

	return(p_time);
}



int do_stat(char *);

int main(int argc, char *argv[])
{
	int i;

	/*
	 * need at least one file name
	 */
	if (argc == 1){
		fprintf(stderr, "Usage: %s file1 [ ... ]\n", argv[0]);
		(void) exit(1);
	}

	/*
	 * do the files one at a time
	 */
	for(i = 1; i < argc; i++){
		if (do_stat(argv[i]) && i+1 != argc)
			printf("====================\n");
	}

	/*
	 * all done!
	 */
	(void) exit(0);
}

/*
 * do the file
 */
int do_stat(char *fname)
{
	struct stat stbuf;
	char *lname;
	ssize_t r;

	/*
	 * get the file information and complain on error
	 */
	if (lstat(fname, &stbuf) < 0){
		perror(fname);
		return(0);
	}

	/*
	 * print file name
	 */
	printf("%s:\n", fname);

	/*
	 * print file system information
	 */
	printf("\t* File system information:\n");
	printf("\t  Major ID of file's device: %d\n", (int) major(stbuf.st_dev));
	printf("\t  Minor ID of file's device: %d\n", (int) minor(stbuf.st_dev));
	printf("\t  Optimal file system blocksize: %ld\n", (long) stbuf.st_blksize);
	printf("\t  File inode number %ld\n", (long) stbuf.st_ino);
	if (S_ISBLK(stbuf.st_mode) || S_ISCHR(stbuf.st_mode)){
		printf("\t  File is a device interface:\n");
		printf("\t\tMajor device number (driver number): %d\n", (int) major(stbuf.st_rdev));
		printf("\t\tMinor device number (driver dependent): %d\n", (int) minor(stbuf.st_rdev));
	}

	/*
	 * print file type, and target if a symlink
	 */
	printf("\t* File type information:\n");
	printf("\t  File type: %s", ftype(stbuf.st_mode));
	if (S_ISLNK(stbuf.st_mode)){
		printf(" [");
		if ((lname = malloc(stbuf.st_size+1)) == NULL)
			printf("*** Ran out of memory ***");
		else if ((r = readlink(fname, lname, stbuf.st_size + 1)) < 0)
			printf("*** Error reading link name ***");
		else if (r > stbuf.st_size)
			printf("*** Contents changed during read ***");
		else{
			lname[stbuf.st_size] = '\0';
			printf("%s", lname);
		}
		printf("]");
	}
	putchar('\n');
	printf("\t  Number of hard links: %ld\n", (long) stbuf.st_nlink);
	printf("\t  Number of bytes in file: %ld\n", (long) stbuf.st_size);
	printf("\t  Number of 512 byte blocks allocated: %ld\n", (long) stbuf.st_blocks);

	/*
	 * print file permission and ownership information
	 */
	printf("\t* File permission information:\n");
	printf("\t  User ID: %d\n", (int) stbuf.st_uid);
	printf("\t  Group ID: %d\n", (int) stbuf.st_gid);
	printf("\t  Permissions: %04o\n", (int) (stbuf.st_mode&07777));
	printf("\t\tUser can %s\n", perm(USER, stbuf.st_mode));
	printf("\t\tGroup can %s\n", perm(GROUP, stbuf.st_mode));
	printf("\t\tEveryone else can %s\n", perm(OTHER, stbuf.st_mode));
	printf("\t* File privileges: %s\n", priv(stbuf.st_mode));
	
	/*
	 * file times
	 */
	printf("\t* File time information:\n");
	printf("\t  Time of last access: %s\n", prtime(stbuf.st_atime));
	printf("\t  Time of last modification: %s\n", prtime(stbuf.st_mtime));
	printf("\t  Time of inode creation: %s\n", prtime(stbuf.st_ctime));


	return(1);
}



UC Davis sigil
Matt Bishop
Office: 2209 Watershed Sciences
Phone: +1 (530) 752-8060
Email: mabishop@ucdavis.edu
ECS 36A, Programming & Problem Solving
Version of April 2, 2024 at 12:13PM

You can get the raw source code here.

Valid HTML 4.01 Transitional Built with BBEdit Built on a Macintosh