statvfs() to get disk free space

Hello all,

I’m trying to get the free space on my disk (in this case /dev/hd0t79) using
statvfs() (I currently get zero from both disk space feilds). I’ve read
through the archives, so I already know that if you give statvfs() the wrong
file it will return correct disk capacity, but wrong free space info.

What am I doing wrong here?

#include <stdio.h>
#include <sys/statvfs.h>


int main()
{

struct statvfs stat_fs;

if (statvfs("/dev/hd0t79", &stat_fs)!=0)
{
perror(“statvfs:”);
exit(0);
}

printf(“Disk space available %ld MB\n”,
(unsigned long) stat_fs.f_bfree * stat_fs.f_bsize);
printf(“Total disk free space %ld MB\n”,
(unsigned long) stat_fs.f_bavail * stat_fs.f_bsize);
exit(0);

}

$ getFree
Disk space available 0 MB
Total disk free space 0 MB
$

Dave McMordie <mcmordie@cim.mcgill.ca> wrote:

I’m trying to get the free space on my disk (in this case /dev/hd0t79)

There is no “free space” on a disk/partition, it is all used; free space
is a filesystem-level logical concept (so target the statvfs() at a
mounted filesystem, like “/fs/hd0-qnx”; to correspond something like
“/dev/hd0t79” to a mountpoint look at devctl(DCMD_FSYS_MOUNTED_BY)).

For a disk/partition, the f_bfree and f_bavail will thus be 0, but
f_blocks will be the total size. For a filesystem f_bfree and f_bavail
will be the amount of free space (to root and a normal user respectively),
and f_blocks the total capacity of the filesystem.

What am I doing wrong here?

Right, makes sense. So I use the compatibility function

int fsys_get_mount_pt(const char *device, char *directory)
{
union {
struct {
struct _io_devctl devctl;
} i;
struct {
struct _io_devctl_reply devctl;
char path[256];
} o;
} msg;

msg.i.devctl.type = _IO_DEVCTL;
msg.i.devctl.combine_len = sizeof(msg.i);
msg.i.devctl.dcmd = DCMD_FSYS_MOUNTED_BY;
msg.i.devctl.nbytes = DCMD_FSYS_MOUNTED_BY >> 16;
msg.i.devctl.zero = 0;
if (_connect_combine(device, 0, O_ACCMODE, SH_DENYNO, 0, _FTYPE_ANY,
sizeof(msg.i), &msg.i, sizeof(msg.o), &msg.o) == -1)
return(-1);
strcpy(directory, msg.o.path);
return(0);
}

to return the correct mount point, which in our case is
/net/XXX.XXX.XXX.XXX/dev/hd0t79. When I feed that to statvfs() I get the
same thing: no space available.

perhaps the problem is:

Caveats:
“The values returned for f_files, f_ffree, and f_favail might not be valid
for NFS-mounted filesystems.”
(http://qdn.qnx.com/support/docs/neutrino_2.11_en/lib_ref/s/statvfs.html)

If this is so, (1) how can I get disk space free on an NFS system and (2)
why is my system NFS-mounted. Is this a good thing?

Somehow df does it. I don’t suppose QSSL would just post the source to
df… :slight_smile:

Thanks,

Dave

“John Garvey” <jgarvey@qnx.com> wrote in message
news:a5rmnj$67g$1@nntp.qnx.com

Dave McMordie <> mcmordie@cim.mcgill.ca> > wrote:
I’m trying to get the free space on my disk (in this case /dev/hd0t79)

There is no “free space” on a disk/partition, it is all used; free space
is a filesystem-level logical concept (so target the statvfs() at a
mounted filesystem, like “/fs/hd0-qnx”; to correspond something like
“/dev/hd0t79” to a mountpoint look at devctl(DCMD_FSYS_MOUNTED_BY)).

For a disk/partition, the f_bfree and f_bavail will thus be 0, but
f_blocks will be the total size. For a filesystem f_bfree and f_bavail
will be the amount of free space (to root and a normal user respectively),
and f_blocks the total capacity of the filesystem.

What am I doing wrong here?

Dave McMordie <mcmordie@cim.mcgill.ca> wrote:

to return the correct mount point, which in our case is
/net/XXX.XXX.XXX.XXX/dev/hd0t79. When I feed that to statvfs() I
get the same thing: no space available.

That is still a block device (a t79 partition), not a logical filesystem
(and so will not have any free blocks as previously pointed out). For
DCMD_FSYS_MOUNTED_BY to have returned a partition, you would have had to
target it at the physical disk ("/dev/hd0") and it be the only partition.
I tried the above code here, with “/dev/hd0t77” mounted at “/hd” and it
translated one into the other correctly, and statvfs() gave expected results.
Where do you think /dev/hd0t79 is mounted at in your specific case?

Somehow df does it.

Well, I wrote df. It has a lot of complexity to iterate the mountpoints
and resolve across union mounts, all of which confuse the basic operation.
The code you posted works for me. What exactly is it you are trying to do;
from previous posts I take it to be - given a partition name, get the free
space on the filesystem it is mounted as? Answer - DCMD_FSYS_MOUNTED_BY
(as in that compatability routine) and statvfs().

Thanks for your quick reply.

Well, I wrote df.

Cool! That means the buck stops here, since df works just fine on my
system.

Okay, I’ve posted my code in its entirety at the bottom, along with output
on our system. We are using a compact flash card for a disk-- I don’t know
much about how it is mounted, but it seems to be via NFS, since the
mounted-by call returns /net/…/dev/hd0t79.

A friend of mine with much better *NIX programming abilities suggested that
statvfs64() might return correctly, and that it should work with any file
except symbolic links, for example “.”. Anyway, what am I doing wrong here?


The code you posted works for me. What exactly is it you are trying to
do;
from previous posts I take it to be - given a partition name, get the free
space on the filesystem it is mounted as? Answer - DCMD_FSYS_MOUNTED_BY
(as in that compatability routine) and statvfs().

Simply put I am trying to find the free space on the disk I’m writing to
before I write to it.

Thanks,

Dave McMordie

Ambulatory Robotics Lab / Centre for Intelligent Machines / McGill
University

mcmordie@cim.mcgill.ca


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/statvfs.h>
#include <devctl.h>
#include <fcntl.h>
#include <sys/dcmd_blk.h>

#include <fcntl.h>
#include <share.h>
#include <string.h>
#include <sys/dcmd_blk.h>
#include <sys/iomsg.h>

// From nto migration kit:
int fsys_get_mount_pt(const char *device, char *directory)
{
union {
struct {
struct _io_devctl devctl;
} i;
struct {
struct _io_devctl_reply devctl;
char path[256];
} o;
} msg;

msg.i.devctl.type = _IO_DEVCTL;
msg.i.devctl.combine_len = sizeof(msg.i);
msg.i.devctl.dcmd = DCMD_FSYS_MOUNTED_BY;
msg.i.devctl.nbytes = DCMD_FSYS_MOUNTED_BY >> 16;
msg.i.devctl.zero = 0;
if (_connect_combine(device, 0, O_ACCMODE, SH_DENYNO, 0, _FTYPE_ANY,
sizeof(msg.i), &msg.i, sizeof(msg.o), &msg.o) == -1)
return(-1);
strcpy(directory, msg.o.path);
return(0);
}

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

int r;
FILE f;
float used;


f = popen(“df -Pk | grep " / " | awk '{print
sprintf(”%f",100
$3/$2)}’", “r”);
fscanf(f, “%f”, &used);
printf(“df returns: disk is %f %% used\n”, used);


int i;
struct statvfs fs;
int return_val;
int fd;
char mount_point[256];
size_t bytes;
int crap;


if (argc<2){
printf(“usage getFree /dev/hd0t79 \n”);
return 0;
}


for (i=1;i<argc;i++){
// Find out where the partition is mounted from
if (fd = open(argv[1], O_RDONLY)==NULL){
perror(“open”);
return -1;
}
fsys_get_mount_pt(argv[1], mount_point);
printf(“return str: %s\n”, mount_point);
fsys_get_mount_pt(mount_point,mount_point );
printf(“return str: %s\n”, mount_point);
fsys_get_mount_pt( mount_point, mount_point);
printf(“return str: %s\n”, mount_point);
fsys_get_mount_pt(mount_point, mount_point);
if ( statvfs(mount_point, &fs) == -1){
perror(“statvfs”);
return -1;
}
printf("%15s\t", mount_point);
printf(“total blocks %d\t”, fs.f_blocks);
printf(“free blocks %d\t”, fs.f_bfree);
printf(“avail blocks %d\n”, fs.f_bavail);
}

return 0;
}


And the output (with IP numbers masked):

$ getFree /dev/hd0t79
df returns: disk is 78.033730 % used
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
/net/x.x.x.x/ total blocks 1 free blocks 0 avail blocks 0

Dave McMordie <mcmordie@cim.mcgill.ca> wrote:

Simply put I am trying to find the free space on the disk I’m writing to
before I write to it.

Then you can simply call statvfs() on the file you intend to write; it
will internally percolate to the top-level filesystem involved. So,
‘statvfs("/the/file/I/am/going/to/write", &fs);’ should correctly fill
‘fs.f_bavail’ with the number of ‘fs.f_frsize’ blocks that are free on
the filesystem that is hosting the named file (without needing to
traverese the mount hierarchy yourself).

if (fd = open(argv[1], O_RDONLY)==NULL){

if ((fd = open(argv[1], O_RDONLY))==-1){
(although fd is unused)

fsys_get_mount_pt(argv[1], mount_point);
fsys_get_mount_pt(mount_point,mount_point );
fsys_get_mount_pt( mount_point, mount_point);

Why three times? Do you have union mounts at “/”? Is one of them NFS?
Can you post the full output of “df”.

$ getFree /dev/hd0t79
df returns: disk is 78.033730 % used
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
/net/x.x.x.x/ total blocks 1 free blocks 0 avail blocks 0

What is ‘fs.f_basetype’ - i.e. who has captured this pathname?

Worky!

Amazing that I missed that. If you try to stat / or /dev/hd0 or /dev/hd0t79
it returns bollocks, but when you stat “.” or any regular file it
works just fine. (Just like you’ve said)

Thanks very much for your help. I’m posting the code in case anyone else
has the same questions.

Quick answers to your questions: I think our CF filesystem is being NFS
mounted for some reason by default. I don’t know what union mounts are, so
I can’t comment there, but I called get_mount_point three times to see if it
returned the same value every time, which it didn’t-- it seemed to coverge
on an NFS fs after two calls.

Anyway, I don’t need to know anything except that stat’ing the file I want
to write to will always return the correct free space.

Thanks again,

Dave

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/statvfs.h>
#include <devctl.h>
#include <fcntl.h>
#include <sys/dcmd_blk.h>


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

float used;
int i;
struct statvfs fs;
int fd;



if (argc<2){
printf(“usage getFree . \n”);
return 0;
}


for (i=1;i<argc;i++){
// Find out where the partition is mounted from
if (fd = open(argv[1], O_RDONLY)==NULL){
perror(“open”);
return -1;
}
if ( statvfs(argv[1], &fs) == -1){
perror(“statvfs”);
return -1;
}
printf(“total %d\t”, fs.f_blocks);
printf(“free %d\t”, fs.f_bfree);
printf(“avail %d\t”, fs.f_bavail);
printf(“type: %s\n”, fs.f_basetype);

}

return 0;
}

output:
$ gcc -o getFree getFree.c
$ getFree .
total 500704 free 147260 avail 147260 type: qnx4

“John Garvey” <jgarvey@qnx.com> wrote in message
news:a7o0p6$cgp$1@nntp.qnx.com

Dave McMordie <> mcmordie@cim.mcgill.ca> > wrote:
Simply put I am trying to find the free space on the disk I’m writing to
before I write to it.

Then you can simply call statvfs() on the file you intend to write; it
will internally percolate to the top-level filesystem involved. So,
‘statvfs("/the/file/I/am/going/to/write", &fs);’ should correctly fill
‘fs.f_bavail’ with the number of ‘fs.f_frsize’ blocks that are free on
the filesystem that is hosting the named file (without needing to
traverese the mount hierarchy yourself).

if (fd = open(argv[1], O_RDONLY)==NULL){

if ((fd = open(argv[1], O_RDONLY))==-1){
(although fd is unused)

fsys_get_mount_pt(argv[1], mount_point);
fsys_get_mount_pt(mount_point,mount_point );
fsys_get_mount_pt( mount_point, mount_point);

Why three times? Do you have union mounts at “/”? Is one of them NFS?
Can you post the full output of “df”.

$ getFree /dev/hd0t79
df returns: disk is 78.033730 % used
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
return str: /net/x.x.x.x/
/net/x.x.x.x/ total blocks 1 free blocks 0 avail blocks 0

What is ‘fs.f_basetype’ - i.e. who has captured this pathname?