diff options
Diffstat (limited to 'agent/mibgroup/host/hr_storage.c')
-rw-r--r-- | agent/mibgroup/host/hr_storage.c | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/agent/mibgroup/host/hr_storage.c b/agent/mibgroup/host/hr_storage.c new file mode 100644 index 0000000..d4716a7 --- /dev/null +++ b/agent/mibgroup/host/hr_storage.c @@ -0,0 +1,785 @@ +/* + * Host Resources MIB - storage group implementation - hr_storage.c + * + */ + +#include <net-snmp/net-snmp-config.h> + +#if defined(freebsd5) +/* undefine these in order to use getfsstat */ +#undef HAVE_STATVFS +#undef STRUCT_STATVFS_HAS_F_FRSIZE +#endif + +#include <sys/types.h> +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if TIME_WITH_SYS_TIME +# ifdef WIN32 +# include <windows.h> +# include <errno.h> +# include <sys/timeb.h> +# else +# include <sys/time.h> +# endif +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if (!defined(mingw32) && !defined(WIN32)) +#if HAVE_UTMPX_H +#include <utmpx.h> +#else +#include <utmp.h> +#endif +#endif /* mingw32 */ +#ifndef dynix +#if HAVE_SYS_VM_H +#include <sys/vm.h> +#if (!defined(KERNEL) || defined(MACH_USER_API)) && defined(HAVE_SYS_VMMETER_H) /*OS X does not #include <sys/vmmeter.h> if (defined(KERNEL) && !defined(MACH_USER_API)) */ +#include <sys/vmmeter.h> +#endif +#else +#if HAVE_VM_VM_H +#include <vm/vm.h> +#if HAVE_MACHINE_TYPES_H +#include <machine/types.h> +#endif +#if HAVE_SYS_VMMETER_H +#include <sys/vmmeter.h> +#endif +#if HAVE_VM_VM_PARAM_H +#include <vm/vm_param.h> +#endif +#else +#if HAVE_SYS_VMPARAM_H +#include <sys/vmparam.h> +#endif +#if HAVE_SYS_VMMAC_H +#include <sys/vmmac.h> +#endif +#if HAVE_SYS_VMMETER_H +#include <sys/vmmeter.h> +#endif +#if HAVE_SYS_VMSYSTM_H +#include <sys/vmsystm.h> +#endif +#endif /* vm/vm.h */ +#endif /* sys/vm.h */ +#if defined(HAVE_UVM_UVM_PARAM_H) && defined(HAVE_UVM_UVM_EXTERN_H) +#include <uvm/uvm_param.h> +#include <uvm/uvm_extern.h> +#elif defined(HAVE_VM_VM_PARAM_H) && defined(HAVE_VM_VM_EXTERN_H) +#include <vm/vm_param.h> +#include <vm/vm_extern.h> +#endif +#if HAVE_KVM_H +#include <kvm.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#if HAVE_SYS_POOL_H +#if defined(MBPOOL_SYMBOL) && defined(MCLPOOL_SYMBOL) +#define __POOL_EXPOSE +#include <sys/pool.h> +#else +#undef HAVE_SYS_POOL_H +#endif +#endif +#if HAVE_SYS_MBUF_H +#include <sys/mbuf.h> +#endif +#if HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#if defined(CTL_HW) && defined(HW_PAGESIZE) +#define USE_SYSCTL +#endif +#if USE_MACH_HOST_STATISTICS +#include <mach/mach.h> +#elif defined(CTL_VM) && (defined(VM_METER) || defined(VM_UVMEXP)) +#define USE_SYSCTL_VM +#endif +#endif /* if HAVE_SYS_SYSCTL_H */ +#endif /* ifndef dynix */ + +#if (defined(aix4) || defined(aix5) || defined(aix6)) && HAVE_LIBPERFSTAT_H +#include <libperfstat.h> +#endif + + +#include "host_res.h" +#include "hr_storage.h" +#include "hr_filesys.h" +#include <net-snmp/agent/auto_nlist.h> + +#if HAVE_MNTENT_H +#include <mntent.h> +#endif +#if HAVE_SYS_MNTTAB_H +#include <sys/mnttab.h> +#endif +#if HAVE_SYS_STATVFS_H +#include <sys/statvfs.h> +#endif +#if HAVE_SYS_VFS_H +#include <sys/vfs.h> +#endif +#if HAVE_SYS_MOUNT_H +#ifdef __osf__ +#undef m_next +#undef m_data +#endif +#include <sys/mount.h> +#endif +#ifdef HAVE_MACHINE_PARAM_H +#include <machine/param.h> +#endif +#include <sys/stat.h> + +#if defined(hpux10) || defined(hpux11) +#include <sys/pstat.h> +#endif +#if defined(solaris2) +#if HAVE_SYS_SWAP_H +#include <sys/swap.h> +#endif +#endif + +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#include <net-snmp/utilities.h> +#include <net-snmp/output_api.h> + +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/agent/hardware/memory.h> + +#ifdef solaris2 +#include "kernel_sunos5.h" +#endif + +#include <net-snmp/agent/agent_read_config.h> +#include <net-snmp/library/read_config.h> + +#define HRSTORE_MONOTONICALLY_INCREASING + + /********************* + * + * Kernel & interface information, + * and internal forward declarations + * + *********************/ + + +#ifdef solaris2 + +extern struct mnttab *HRFS_entry; +#define HRFS_mount mnt_mountp +#define HRFS_statfs statvfs +#define HRFS_HAS_FRSIZE STRUCT_STATVFS_HAS_F_FRSIZE + +#elif defined(WIN32) +/* fake block size */ +#define FAKED_BLOCK_SIZE 512 + +extern struct win_statfs *HRFS_entry; +#define HRFS_statfs win_statfs +#define HRFS_mount f_driveletter + +#elif defined(HAVE_STATVFS) && defined(__NetBSD__) + +extern struct statvfs *HRFS_entry; +extern int fscount; +#define HRFS_statfs statvfs +#define HRFS_mount f_mntonname +#define HRFS_HAS_FRSIZE STRUCT_STATVFS_HAS_F_FRSIZE + +#elif defined(HAVE_STATVFS) && defined(STRUCT_STATVFS_HAS_MNT_DIR) + +extern struct mntent *HRFS_entry; +extern int fscount; +#define HRFS_statfs statvfs +#define HRFS_mount mnt_dir +#define HRFS_HAS_FRSIZE STRUCT_STATVFS_HAS_F_FRSIZE + +#elif defined(HAVE_GETFSSTAT) + +extern struct statfs *HRFS_entry; +extern int fscount; +#define HRFS_statfs statfs +#define HRFS_mount f_mntonname +#define HRFS_HAS_FRSIZE STRUCT_STATFS_HAS_F_FRSIZE + +#else + +extern struct mntent *HRFS_entry; +#define HRFS_mount mnt_dir +#define HRFS_statfs statfs +#define HRFS_HAS_FRSIZE STRUCT_STATFS_HAS_F_FRSIZE + +#endif + +#if defined(USE_MACH_HOST_STATISTICS) +mach_port_t myHost; +#endif + +static void parse_storage_config(const char *, char *); + + /********************* + * + * Initialisation & common implementation functions + * + *********************/ +int Get_Next_HR_Store(void); +void Init_HR_Store(void); +int header_hrstore(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +void* header_hrstoreEntry(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +#ifdef solaris2 +void sol_get_swapinfo(int *, int *); +#endif + +#define HRSTORE_MEMSIZE 1 +#define HRSTORE_INDEX 2 +#define HRSTORE_TYPE 3 +#define HRSTORE_DESCR 4 +#define HRSTORE_UNITS 5 +#define HRSTORE_SIZE 6 +#define HRSTORE_USED 7 +#define HRSTORE_FAILS 8 + +struct variable4 hrstore_variables[] = { + {HRSTORE_MEMSIZE, ASN_INTEGER, RONLY, var_hrstore, 1, {2}}, + {HRSTORE_INDEX, ASN_INTEGER, RONLY, var_hrstore, 3, {3, 1, 1}}, + {HRSTORE_TYPE, ASN_OBJECT_ID, RONLY, var_hrstore, 3, {3, 1, 2}}, + {HRSTORE_DESCR, ASN_OCTET_STR, RONLY, var_hrstore, 3, {3, 1, 3}}, + {HRSTORE_UNITS, ASN_INTEGER, RONLY, var_hrstore, 3, {3, 1, 4}}, + {HRSTORE_SIZE, ASN_INTEGER, RONLY, var_hrstore, 3, {3, 1, 5}}, + {HRSTORE_USED, ASN_INTEGER, RONLY, var_hrstore, 3, {3, 1, 6}}, + {HRSTORE_FAILS, ASN_COUNTER, RONLY, var_hrstore, 3, {3, 1, 7}} +}; +oid hrstore_variables_oid[] = { 1, 3, 6, 1, 2, 1, 25, 2 }; + + +void +init_hr_storage(void) +{ + char *appname; + + REGISTER_MIB("host/hr_storage", hrstore_variables, variable4, + hrstore_variables_oid); + + appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_APPTYPE); + netsnmp_ds_register_config(ASN_BOOLEAN, appname, "skipNFSInHostResources", + NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_SKIPNFSINHOSTRESOURCES); + + snmpd_register_config_handler("storageUseNFS", parse_storage_config, NULL, + "1 | 2\t\t(1 = enable, 2 = disable)"); +} + +static int storageUseNFS = 1; /* Default to reporting NFS mounts as NetworkDisk */ + +static void +parse_storage_config(const char *token, char *cptr) +{ + char *val; + int ival; + char *st; + + val = strtok_r(cptr, " \t", &st); + if (!val) { + config_perror("Missing FLAG parameter in storageUseNFS"); + return; + } + ival = atoi(val); + if (ival < 1 || ival > 2) { + config_perror("storageUseNFS must be 1 or 2"); + return; + } + storageUseNFS = (ival == 1) ? 1 : 0; +} + +/* + * header_hrstore(... + * Arguments: + * vp IN - pointer to variable entry that points here + * name IN/OUT - IN/name requested, OUT/name found + * length IN/OUT - length of IN/OUT oid's + * exact IN - TRUE if an exact match was requested + * var_len OUT - length of variable or 0 if function returned + * write_method + * + */ + +int +header_hrstore(struct variable *vp, + oid * name, + size_t * length, + int exact, size_t * var_len, WriteMethod ** write_method) +{ +#define HRSTORE_NAME_LENGTH 9 + oid newname[MAX_OID_LEN]; + int result; + + DEBUGMSGTL(("host/hr_storage", "var_hrstore: ")); + DEBUGMSGOID(("host/hr_storage", name, *length)); + DEBUGMSG(("host/hr_storage", " %d\n", exact)); + + memcpy((char *) newname, (char *) vp->name, vp->namelen * sizeof(oid)); + newname[HRSTORE_NAME_LENGTH] = 0; + result = snmp_oid_compare(name, *length, newname, vp->namelen + 1); + if ((exact && (result != 0)) || (!exact && (result >= 0))) + return (MATCH_FAILED); + memcpy((char *) name, (char *) newname, + (vp->namelen + 1) * sizeof(oid)); + *length = vp->namelen + 1; + + *write_method = 0; + *var_len = sizeof(long); /* default to 'long' results */ + return (MATCH_SUCCEEDED); +} + +void * +header_hrstoreEntry(struct variable *vp, + oid * name, + size_t * length, + int exact, + size_t * var_len, WriteMethod ** write_method) +{ +#define HRSTORE_ENTRY_NAME_LENGTH 11 + oid newname[MAX_OID_LEN]; + int storage_idx, LowIndex = -1; + int result; + int idx = -1; + netsnmp_memory_info *mem = NULL; + + DEBUGMSGTL(("host/hr_storage", "var_hrstoreEntry: request ")); + DEBUGMSGOID(("host/hr_storage", name, *length)); + DEBUGMSG(("host/hr_storage", " exact=%d\n", exact)); + + memcpy((char *) newname, (char *) vp->name, + (int) vp->namelen * sizeof(oid)); + result = snmp_oid_compare(name, *length, vp->name, vp->namelen); + + DEBUGMSGTL(("host/hr_storage", "var_hrstoreEntry: compare ")); + DEBUGMSGOID(("host/hr_storage", vp->name, vp->namelen)); + DEBUGMSG(("host/hr_storage", " => %d\n", result)); + + + if (result < 0 || + *length <= HRSTORE_ENTRY_NAME_LENGTH ) { + /* + * Requested OID too early or too short to refer + * to a valid row (for the current column object). + * GET requests should fail, GETNEXT requests + * should use the first row. + */ + if ( exact ) + return NULL; + netsnmp_memory_load(); + mem = netsnmp_memory_get_first( 0 ); + } + else { + /* + * Otherwise, retrieve the requested + * (or following) row as appropriate. + */ + if ( exact && *length > HRSTORE_ENTRY_NAME_LENGTH+1 ) + return NULL; /* Too long for a valid instance */ + idx = name[ HRSTORE_ENTRY_NAME_LENGTH ]; + if ( idx < NETSNMP_MEM_TYPE_MAX ) { + netsnmp_memory_load(); + mem = ( exact ? netsnmp_memory_get_byIdx( idx, 0 ) : + netsnmp_memory_get_next_byIdx( idx, 0 )); + } + } + + /* + * If this matched a memory-based entry, then + * update the OID parameter(s) for GETNEXT requests. + */ + if ( mem ) { + if ( !exact ) { + newname[ HRSTORE_ENTRY_NAME_LENGTH ] = mem->idx; + memcpy((char *) name, (char *) newname, + ((int) vp->namelen + 1) * sizeof(oid)); + *length = vp->namelen + 1; + } + } + /* + * If this didn't match a memory-based entry, + * then consider the disk-based storage. + */ + else { + Init_HR_Store(); + for (;;) { + storage_idx = Get_Next_HR_Store(); + DEBUGMSG(("host/hr_storage", "(index %d ....", storage_idx)); + if (storage_idx == -1) + break; + newname[HRSTORE_ENTRY_NAME_LENGTH] = storage_idx; + DEBUGMSGOID(("host/hr_storage", newname, *length)); + DEBUGMSG(("host/hr_storage", "\n")); + result = snmp_oid_compare(name, *length, newname, vp->namelen + 1); + if (exact && (result == 0)) { + LowIndex = storage_idx; + /* + * Save storage status information + */ + break; + } + if ((!exact && (result < 0)) && + (LowIndex == -1 || storage_idx < LowIndex)) { + LowIndex = storage_idx; + /* + * Save storage status information + */ +#ifdef HRSTORE_MONOTONICALLY_INCREASING + break; +#endif + } + } + if ( LowIndex != -1 ) { + if ( !exact ) { + newname[ HRSTORE_ENTRY_NAME_LENGTH ] = LowIndex; + memcpy((char *) name, (char *) newname, + ((int) vp->namelen + 1) * sizeof(oid)); + *length = vp->namelen + 1; + } + mem = (netsnmp_memory_info*)0xffffffff; /* To indicate 'success' */ + } + } + + *write_method = 0; + *var_len = sizeof(long); /* default to 'long' results */ + + /* + * ... and return the appropriate row + */ + DEBUGMSGTL(("host/hr_storage", "var_hrstoreEntry: process ")); + DEBUGMSGOID(("host/hr_storage", name, *length)); + DEBUGMSG(("host/hr_storage", " (%x)\n", mem)); + return (void*)mem; +} + +oid storage_type_id[] = { 1, 3, 6, 1, 2, 1, 25, 2, 1, 1 }; /* hrStorageOther */ +int storage_type_len = + sizeof(storage_type_id) / sizeof(storage_type_id[0]); + + /********************* + * + * System specific implementation functions + * + *********************/ + +static const char *hrs_descr[] = { + NULL, + "Memory Buffers", /* HRS_TYPE_MBUF */ + "Real Memory", /* HRS_TYPE_MEM */ + "Swap Space" /* HRS_TYPE_SWAP */ +}; + + + +u_char * +var_hrstore(struct variable *vp, + oid * name, + size_t * length, + int exact, size_t * var_len, WriteMethod ** write_method) +{ + int store_idx = 0; + static char string[1024]; + struct HRFS_statfs stat_buf; + void *ptr; + netsnmp_memory_info *mem = NULL; + + if (vp->magic == HRSTORE_MEMSIZE) { + if (header_hrstore(vp, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + } else { + +really_try_next: + ptr = header_hrstoreEntry(vp, name, length, exact, var_len, + write_method); + if (ptr == NULL) + return NULL; + + store_idx = name[ HRSTORE_ENTRY_NAME_LENGTH ]; + if (HRFS_entry && + store_idx > NETSNMP_MEM_TYPE_MAX && + netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_SKIPNFSINHOSTRESOURCES) && + Check_HR_FileSys_NFS()) + return NULL; + if (store_idx > NETSNMP_MEM_TYPE_MAX ) { + if (HRFS_statfs(HRFS_entry->HRFS_mount, &stat_buf) < 0) { + snmp_log_perror(HRFS_entry->HRFS_mount); + goto try_next; + } + } else { + mem = (netsnmp_memory_info*)ptr; + } + } + + + + switch (vp->magic) { + case HRSTORE_MEMSIZE: + netsnmp_memory_load(); + mem = netsnmp_memory_get_byIdx( NETSNMP_MEM_TYPE_PHYSMEM, 0 ); + if ( !mem || mem->size == -1 || mem->units == -1 ) + return NULL; + long_return = mem->size * (mem->units / 1024); + return (u_char *) & long_return; + + case HRSTORE_INDEX: + long_return = store_idx; + return (u_char *) & long_return; + case HRSTORE_TYPE: + if (store_idx > NETSNMP_MEM_TYPE_MAX) + if (storageUseNFS && Check_HR_FileSys_NFS()) + storage_type_id[storage_type_len - 1] = 10; /* Network Disk */ +#if HAVE_HASMNTOPT && !(defined(aix4) || defined(aix5) || defined(aix6)) + /* + * hasmntopt takes "const struct mntent*", but HRFS_entry has been + * defined differently for AIX, so skip this for AIX + */ + else if (hasmntopt(HRFS_entry, "loop") != NULL) + storage_type_id[storage_type_len - 1] = 5; /* Removable Disk */ +#endif + else + storage_type_id[storage_type_len - 1] = 4; /* Assume fixed */ + else + switch (store_idx) { + case NETSNMP_MEM_TYPE_PHYSMEM: + case NETSNMP_MEM_TYPE_USERMEM: + storage_type_id[storage_type_len - 1] = 2; /* RAM */ + break; + case NETSNMP_MEM_TYPE_VIRTMEM: + case NETSNMP_MEM_TYPE_SWAP: + storage_type_id[storage_type_len - 1] = 3; /* Virtual Mem */ + break; + default: + storage_type_id[storage_type_len - 1] = 1; /* Other */ + break; + } + *var_len = sizeof(storage_type_id); + return (u_char *) storage_type_id; + case HRSTORE_DESCR: + if (store_idx > NETSNMP_MEM_TYPE_MAX) { + strncpy(string, HRFS_entry->HRFS_mount, sizeof(string)-1); + string[ sizeof(string)-1 ] = 0; + *var_len = strlen(string); + return (u_char *) string; + } else { + if ( !mem || !mem->descr ) + goto try_next; + *var_len = strlen(mem->descr); + return (u_char *) mem->descr; + } + case HRSTORE_UNITS: + if (store_idx > NETSNMP_MEM_TYPE_MAX) +#if HRFS_HAS_FRSIZE + long_return = stat_buf.f_frsize; +#else + long_return = stat_buf.f_bsize; +#endif + else { + if ( !mem || mem->units == -1 ) + goto try_next; + long_return = mem->units; + } + return (u_char *) & long_return; + case HRSTORE_SIZE: + if (store_idx > NETSNMP_MEM_TYPE_MAX) + long_return = stat_buf.f_blocks; + else { + if ( !mem || mem->size == -1 ) + goto try_next; + long_return = mem->size; + } + return (u_char *) & long_return; + case HRSTORE_USED: + if (store_idx > NETSNMP_MEM_TYPE_MAX) + long_return = (stat_buf.f_blocks - stat_buf.f_bfree); + else { + if ( !mem || mem->size == -1 || mem->free == -1 ) + goto try_next; + long_return = mem->size - mem->free; + } + return (u_char *) & long_return; + case HRSTORE_FAILS: + if (store_idx > NETSNMP_MEM_TYPE_MAX) +#if NETSNMP_NO_DUMMY_VALUES + goto try_next; +#else + long_return = 0; +#endif + else { + if ( !mem || mem->other == -1 ) + goto try_next; + long_return = mem->other; + } + return (u_char *) & long_return; + default: + DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_hrstore\n", + vp->magic)); + } + return NULL; + + try_next: + if (!exact) + goto really_try_next; + + return NULL; +} + + + /********************* + * + * Internal implementation functions + * + *********************/ + +static int FS_storage; +static int HRS_index; + +void +Init_HR_Store(void) +{ + HRS_index = 0; + Init_HR_FileSys(); +} + +int +Get_Next_HR_Store(void) +{ + /* + * File-based storage + */ + for (;;) { + HRS_index = Get_Next_HR_FileSys(); + if (HRS_index >= 0) { + if (!(netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_SKIPNFSINHOSTRESOURCES) && + Check_HR_FileSys_NFS())) { + return HRS_index + NETSNMP_MEM_TYPE_MAX; + } + } else { + return -1; + } + } +} + +#ifdef solaris2 +void +sol_get_swapinfo(int *totalP, int *usedP) +{ + struct anoninfo ainfo; + + if (swapctl(SC_AINFO, &ainfo) < 0) { + *totalP = *usedP = 0; + return; + } + + *totalP = ainfo.ani_max; + *usedP = ainfo.ani_resv; +} +#endif /* solaris2 */ + +#ifdef WIN32 +char *win_realpath(const char *file_name, char *resolved_name) +{ + char szFile[_MAX_PATH + 1]; + char *pszRet; + + pszRet = _fullpath(szFile, resolved_name, MAX_PATH); + + return pszRet; +} + +static int win_statfs (const char *path, struct win_statfs *buf) +{ + HINSTANCE h; + FARPROC f; + int retval = 0; + char tmp [MAX_PATH], resolved_path [MAX_PATH]; + GetFullPathName(path, MAX_PATH, resolved_path, NULL); + /* TODO - Fix this! The realpath macro needs defined + * or rewritten into the function. + */ + + win_realpath(path, resolved_path); + + if (!resolved_path) + retval = - 1; + else { + /* check whether GetDiskFreeSpaceExA is supported */ + h = LoadLibraryA ("kernel32.dll"); + if (h) + f = GetProcAddress (h, "GetDiskFreeSpaceExA"); + else + f = NULL; + + if (f) { + ULARGE_INTEGER bytes_free, bytes_total, bytes_free2; + if (!f (resolved_path, &bytes_free2, &bytes_total, &bytes_free)) { + errno = ENOENT; + retval = - 1; + } else { + buf -> f_bsize = FAKED_BLOCK_SIZE; + buf -> f_bfree = (bytes_free.QuadPart) / FAKED_BLOCK_SIZE; + buf -> f_files = buf -> f_blocks = (bytes_total.QuadPart) / FAKED_BLOCK_SIZE; + buf -> f_ffree = buf -> f_bavail = (bytes_free2.QuadPart) / FAKED_BLOCK_SIZE; + } + } else { + DWORD sectors_per_cluster, bytes_per_sector; + if (h) FreeLibrary (h); + if (!GetDiskFreeSpaceA (resolved_path, §ors_per_cluster, + &bytes_per_sector, &buf -> f_bavail, &buf -> f_blocks)) { + errno = ENOENT; + retval = - 1; + } else { + buf -> f_bsize = sectors_per_cluster * bytes_per_sector; + buf -> f_files = buf -> f_blocks; + buf -> f_ffree = buf -> f_bavail; + buf -> f_bfree = buf -> f_bavail; + } + } + if (h) FreeLibrary (h); + } + + /* get the FS volume information */ + if (strspn (":", resolved_path) > 0) resolved_path [3] = '\0'; /* we want only the root */ + if (GetVolumeInformation (resolved_path, NULL, 0, &buf -> f_fsid, &buf -> f_namelen, + NULL, tmp, MAX_PATH)) { + if (strcasecmp ("NTFS", tmp) == 0) { + buf -> f_type = NTFS_SUPER_MAGIC; + } else { + buf -> f_type = MSDOS_SUPER_MAGIC; + } + } else { + errno = ENOENT; + retval = - 1; + } + return retval; +} +#endif /* WIN32 */ |