[comp.unix.questions] checking disk space from c program

russ@wpg.UUCP (Russell Lawrence) (04/08/88)

I'm stumped.  Apart from using system() to run 'df', could someone 
please suggest a method for checking available disk space from a 
c program?

Thanks.

-- 
Russell Lawrence, WP Group, POB 306, Metairie, LA 70004
AT&T:   +1 504 456 0001          COMPUSERVE: 72337,3261
UUCP:   {philabs,hpda,nbires,amdahl,...}!uunet!wpg!russ

ford@kenobi.UUCP (Mike Ditto) (04/10/88)

Posting-Front-End: GNU Emacs 18.41.10 of Fri Oct  2 1987 on kenobi (usg-unix-v)


In article <398@wpg.UUCP> russ@wpg.UUCP (Russell Lawrence) writes:

> I'm stumped.  Apart from using system() to run 'df', could someone 
> please suggest a method for checking available disk space from a 
> c program?

Read /etc/mnttab to see what devices are mounted, and use the ustat
system call to check the status of each.  Or, if you are wondering how
big you can make a certain file, you can also stat() the file (or the
directory in which you intend to create it) to see what device it is
on, and ustat() that.

See mnttab(4), stat(2), ustat(2).

					-=] Ford [=-

"Once there were parking lots,		(In Real Life:  Mike Ditto)
now it's a peaceful oasis.		ford%kenobi@crash.CTS.COM
This was a Pizza Hut,			...!sdcsvax!crash!kenobi!ford
now it's all covered with daisies." -- Talking Heads

gandalf@csli.STANFORD.EDU (Juergen Wagner) (04/10/88)

Yes, there is a way to find out the space available on the file system
a particular file resides on. The only problem is that you have to setuid to 
root to be able to read the disk device. Normal users will have to use df.

Here comes a small program which does the job (usage: "d <file>" tells you
the free space on the file system of <file>).

Juergen "Gandalf" Wagner,		   gandalf@csli.stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA

/*
** Unpack this into d.c
** $Compile: cc -g -o d d.c
*/

# include <stdio.h>
# include <sys/param.h>
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <sys/file.h>
# include <ufs/fs.h>
# include <fstab.h>

union {
	struct fs iu_fs;
	char dummy[SBSIZE];
} sb;
# define sblock sb.iu_fs

static char *getfs(file,dev)
char *file;
int dev;
{
	struct stat s;
	struct fstab *fs;

	if (dev > 0) {
		setfsent();
		while (fs = getfsent()) {
			if (stat(fs->fs_spec, &s))
				continue;
			if (s.st_rdev == dev) {
				endfsent();
				return(fs->fs_spec);
			}
		}
	} else return(file);
}

main(argc, argv)
int argc;
char **argv;
{
	char *file = argv[1];
	int f;
	int totalblks, free, used, availblks, avail;
	int freeblks;
	struct stat s;

	if (stat(file, &s)) {
	  perror(file);
	  exit(1);
	}
	if ((s.st_mode & S_IFMT) != S_IFBLK)
	  file = getfs(file,s.st_dev);
	if (stat(file, &s)) {
	  perror(file);
	  exit(1);
	}
	if ((s.st_mode & S_IFMT) != S_IFBLK) {
	  printf("%s is not a block-oriented device", file);
	  exit(1);
	}
	f = open(file, O_RDONLY);
	if (f < 0) {
	  perror(file);
	  exit(1);
	}
	lseek(f, (long) (SBLOCK * DEV_BSIZE), 0);
	read(f, &sblock, SBSIZE);

	totalblks = sblock.fs_dsize;
	free = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
	       sblock.fs_cstotal.cs_nffree;
	used = totalblks - free;
	availblks = totalblks * (100 - sblock.fs_minfree) / 100;
	avail = availblks > used ? availblks - used : 0;
	freeblks = avail * sblock.fs_fsize / 1024;
	printf("free of %s: %d\n", file, freeblks);
	exit(0);
}
-- 
Juergen "Gandalf" Wagner,		   gandalf@csli.stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA

zjat02@apctrc.UUCP (Jon A. Tankersley) (04/10/88)

On a Sun maybe on any BSD system - statfs has the information.
Here is a skeleton:
#include <sys/types.h>
#include <sys/vfs.h>
    .
    .
    .
    struct statfs buf;
/*
 * call statfs regarding a filename path and check out the results
 * filename MUST exist as either file or directory
 */
    if ( statfs(path,&buf) == 0 )
	/* return block_size*blocks_avail as size in bytes */
	return(buf.f_bsize*buf.f_bavail);
    else
	/* error, return -1 */
	return(-1);



-tank-

friedl@vsi.UUCP (Stephen J. Friedl) (04/11/88)

In article <3431@csli.STANFORD.EDU>, gandalf@csli.STANFORD.EDU (Juergen Wagner) writes:
> Yes, there is a way to find out the space available on the file system
> a particular file resides on. The only problem is that you have to setuid to 
> root to be able to read the disk device. Normal users will have to use df.
> 
> Here comes a small program which does the job (usage: "d <file>" tells you
> the free space on the file system of <file>).
>
> [small program text here]

No comments on the code included, but you might not need to
make the program setuid root.  Many systems have their disk
devices owned and grouped (grouped?) by other users.

   0 brw-r-----   1 root   sys   17, 16 Jun 27  1987 /dev/dsk/c1d1s0

If you are writing your program to include this code, look at
the group (and mode, of course) to see if you can exploit a
lesser id.  In the above example, the program could be set-group-id
`sys' rather than set-user-id `root'.  Better from a lot of points
is to just popen("df") (or the sample "d" program) and read the
output; then the program is still safe and effective.

Please do not take setuid or setgid lightly.  You may be writing
for some small single-user machine (your Unix PC, an AT with
uport, etc.), but you will move on someday.  Or, perhaps, you
will submit your program to the net and lots of other people on
more lively machines will use it.   If you make it a habit to not
put in security holes when it doesn't matter, then you will be
that much farther ahead when it does matter.  BTW, portability
works the same way...

I welcome all questions on setuid/setgid topics via email.

-- 
Steve Friedl   V-Systems, Inc.   "Yes, I'm jeff@unh's brother"
friedl@vsi.com  {backbones}!vsi.com!friedl  attmail!vsi!friedl

gandalf@csli.STANFORD.EDU (Juergen Wagner) (04/11/88)

In article <71@kenobi.UUCP> ford@kenobi.UUCP (Mike Ditto) writes:
>...
>Read /etc/mnttab to see what devices are mounted, and use the ustat
>system call to check the status of each.  Or, if you are wondering how
>big you can make a certain file, you can also stat() the file (or the
>directory in which you intend to create it) to see what device it is
>on, and ustat() that.
>...

The problem with ustat is that it doesn't exist on BSD systems (even on
Suns), and the problem with stat is that you can't get the whole 
information without actually reading the super-block, i.e. without having
root privileges. Normal users have to stick with "df" pipes thru some
filter.

Juergen "Gandalf" Wagner,		   gandalf@csli.stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA

Disclaimer: The opinions expressed herein are those of my cat.
-- 
Juergen "Gandalf" Wagner,		   gandalf@csli.stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA

dieter@titan.nmt.edu (Dieter Muller) (04/11/88)

In article <3448@csli.STANFORD.EDU> gandalf@csli.stanford.edu (Juergen Wagner) writes:
>In article <71@kenobi.UUCP> ford@kenobi.UUCP (Mike Ditto) writes:
>>...
>>Read /etc/mnttab to see what devices are mounted, and use the ustat
>>system call to check the status of each....
>>...
>
>The problem with ustat is that it doesn't exist on BSD systems (even on
>Suns), and the problem with stat is that you can't get the whole 
>information without actually reading the super-block, i.e. without having
>root privileges. Normal users have to stick with "df" pipes thru some
>filter.

If you have a Sun, what's wrong with statfs?  True, it isn't on our
4.3BSD Vax, and it doesn't seem real happy with NFS, but it's there
and it works (SunOS 3.5).

Here's an example:

	Filesystem:  /titan1
	Block size:  1024
	Num blocks:  490369
	Total free:  97943
	Avail free:  48906
	Total nodes: 96256
	Free nodes:  77991

Looks like exactly what was asked for.  Yes, I know all the world's not
a Sun.  The code for this example follows.  I didn't run it as root.

Dieter Muller
----------------
#include <stdio.h>
#include <sys/types.h>
#include <sys/vfs.h>

main ()
{
	struct statfs buffer;

	if (statfs ("/titan1", & buffer) == -1)
	    perror ("/titan1");
	else print_statfs ("/titan1", & buffer);

	exit (0);
}

print_statfs (name, buffer)
char * name;
struct statfs * buffer;
{
	printf ("Filesystem:  %s\n", name);
	printf ("Block size:  %d\n", buffer -> f_bsize);
	printf ("Num blocks:  %d\n", buffer -> f_blocks);
	printf ("Total free:  %d\n", buffer -> f_bfree);
	printf ("Avail free:  %d\n", buffer -> f_bavail);
	printf ("Total nodes: %d\n", buffer -> f_files);
	printf ("Free nodes:  %d\n", buffer -> f_ffree);

	fflush (stdout);
	
	return;
}
-- 
...{cmcl2, ihnp4}!lanl!unm-la!unmvax!nmtsun!dieter
...gemini!crunch!unmvax!nmtsun!dieter
dieter@nmtsun.nmt.edu