/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*/
#ifndef WIN32
# ifdef _AIX
# define _LARGE_FILES
# else
# define _FILE_OFFSET_BITS 64
# define _LARGEFILE64_SOURCE
# endif
#endif
#include "sigar.h"
#ifndef WIN32
#if defined(__FreeBSD__) || defined(__OpenBSD__)
# include
# include
#else
# include
# define HAVE_STATVFS
#endif
#include
#define SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) ((val * bsize) >> 1)
int sigar_statvfs(sigar_t *sigar,
const char *dirname,
sigar_file_system_usage_t *fsusage)
{
sigar_uint64_t val, bsize;
#ifdef HAVE_STATVFS
struct statvfs buf;
int status =
# if defined(__sun) && !defined(_LP64)
/* http://bugs.opensolaris.org/view_bug.do?bug_id=4462986 */
statvfs(dirname, (void *)&buf);
# else
statvfs(dirname, &buf);
# endif
#else
struct statfs buf;
int status = statfs(dirname, &buf);
#endif
if (status != 0) {
return errno;
}
#ifdef HAVE_STATVFS
bsize = buf.f_frsize / 512;
#else
bsize = buf.f_bsize / 512;
#endif
val = buf.f_blocks;
fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
val = buf.f_bfree;
fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
val = buf.f_bavail;
fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
fsusage->used = fsusage->total - fsusage->free;
fsusage->files = buf.f_files;
fsusage->free_files = buf.f_ffree;
return SIGAR_OK;
}
#endif
/*
* whittled down version of apr/file_info/{unix,win32}/filestat.c
* to fillin sigar_fileattrs_t
*/
#include "sigar_fileinfo.h"
#include "sigar_log.h"
#ifndef SIGAR_ZERO
#define SIGAR_ZERO(s) \
memset(s, '\0', sizeof(*(s)))
#endif
#ifdef WIN32
#include
sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft);
#else
#include
#endif
static const char* types[] = {
"none",
"regular",
"directory",
"character device",
"block device",
"pipe",
"symbolic link",
"socket",
"unknown"
};
SIGAR_DECLARE(const char *)
sigar_file_attrs_type_string_get(sigar_file_type_e type)
{
if ((type < SIGAR_FILETYPE_NOFILE) ||
(type > SIGAR_FILETYPE_UNKFILE))
{
type = SIGAR_FILETYPE_UNKFILE;
}
return types[type];
}
static const sigar_uint64_t perm_modes[] = {
SIGAR_UREAD, SIGAR_UWRITE, SIGAR_UEXECUTE,
SIGAR_GREAD, SIGAR_GWRITE, SIGAR_GEXECUTE,
SIGAR_WREAD, SIGAR_WWRITE, SIGAR_WEXECUTE
};
static const char perm_chars[] = "rwx";
SIGAR_DECLARE(char *)
sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions,
char *str)
{
char *ptr = str;
int i=0, j=0;
for (i=0; i<9; i+=3) {
for (j=0; j<3; j++) {
if (permissions & perm_modes[i+j]) {
*ptr = perm_chars[j];
}
else {
*ptr = '-';
}
ptr++;
}
}
*ptr = '\0';
return str;
}
static const int perm_int[] = {
400, 200, 100,
40, 20, 10,
4, 2, 1
};
SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions)
{
int i=0;
int perms = 0;
/* no doubt there is some fancy bitshifting
* to convert, but this works fine.
*/
for (i=0; i<9; i++) {
if (permissions & perm_modes[i]) {
perms += perm_int[i];
}
}
return perms;
}
#define IS_DOTDIR(dir) \
((dir[0] == '.') && (!dir[1] || ((dir[1] == '.') && !dir[2])))
#define DIR_STAT_WARN() \
sigar_log_printf(sigar, SIGAR_LOG_WARN, \
"dir_stat: cannot stat `%s': %s", \
name, \
sigar_strerror(sigar, status))
#if defined(NETWARE)
int sigar_dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats)
{
return SIGAR_ENOTIMPL;
}
int sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return SIGAR_ENOTIMPL;
}
int sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return SIGAR_ENOTIMPL;
}
#elif defined(WIN32)
#include
#include
static void fillin_fileattrs(sigar_file_attrs_t *finfo,
WIN32_FILE_ATTRIBUTE_DATA *wininfo,
int linkinfo)
{
DWORD *sizes = &wininfo->nFileSizeHigh;
finfo->atime = sigar_FileTimeToTime(&wininfo->ftLastAccessTime) / 1000;
finfo->ctime = sigar_FileTimeToTime(&wininfo->ftCreationTime) / 1000;
finfo->mtime = sigar_FileTimeToTime(&wininfo->ftLastWriteTime) / 1000;
finfo->size =
(sigar_uint64_t)sizes[1] | ((sigar_uint64_t)sizes[0] << 32);
if (linkinfo &&
(wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
finfo->type = SIGAR_FILETYPE_LNK;
}
else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
finfo->type = SIGAR_FILETYPE_DIR;
}
else {
finfo->type = SIGAR_FILETYPE_REG;
}
}
static sigar_uint64_t convert_perms(ACCESS_MASK acc, sigar_uint64_t scope)
{
sigar_uint64_t perms = 0;
if (acc & FILE_EXECUTE) {
perms |= SIGAR_WEXECUTE;
}
if (acc & FILE_WRITE_DATA) {
perms |= SIGAR_WWRITE;
}
if (acc & FILE_READ_DATA) {
perms |= SIGAR_WREAD;
}
return (perms << scope);
}
static int get_security_info(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
DWORD retval;
PSID user = NULL, group = NULL, world = NULL;
PACL dacl = NULL;
PSECURITY_DESCRIPTOR pdesc = NULL;
SECURITY_INFORMATION sinfo =
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION;
TRUSTEE ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
ACCESS_MASK acc;
SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY;
retval = GetNamedSecurityInfo((char *)file,
SE_FILE_OBJECT,
sinfo,
&user,
&group,
&dacl,
NULL,
&pdesc);
if (retval != ERROR_SUCCESS) {
return retval;
}
if (!AllocateAndInitializeSid(&auth, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, &world))
{
world = NULL;
}
ident.TrusteeType = TRUSTEE_IS_USER;
ident.ptstrName = user;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 8);
}
ident.TrusteeType = TRUSTEE_IS_GROUP;
ident.ptstrName = group;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 4);
}
if (world) {
ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ident.ptstrName = world;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 0);
}
}
if (world) {
FreeSid(world);
}
LocalFree(pdesc);
return SIGAR_OK;
}
static int fileattrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs,
int linkinfo)
{
BY_HANDLE_FILE_INFORMATION info;
WIN32_FILE_ATTRIBUTE_DATA attrs;
HANDLE handle;
DWORD flags;
SIGAR_ZERO(fileattrs);
if (!GetFileAttributesExA(file,
GetFileExInfoStandard,
&attrs))
{
return GetLastError();
}
fillin_fileattrs(fileattrs, &attrs, linkinfo);
flags = fileattrs->type == SIGAR_FILETYPE_DIR ?
FILE_FLAG_BACKUP_SEMANTICS :
FILE_ATTRIBUTE_NORMAL;
/**
* We need to set dwDesiredAccess to 0 to work in cases where GENERIC_READ can fail.
*
* see: http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
*/
handle = CreateFile(file,
0,
0,
NULL,
OPEN_EXISTING,
flags,
NULL);
if (handle != INVALID_HANDLE_VALUE) {
if (GetFileInformationByHandle(handle, &info)) {
fileattrs->inode =
info.nFileIndexLow |
(info.nFileIndexHigh << 32);
fileattrs->device = info.dwVolumeSerialNumber;
fileattrs->nlink = info.nNumberOfLinks;
}
CloseHandle(handle);
}
get_security_info(sigar, file, fileattrs);
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return fileattrs_get(sigar, file, fileattrs, 0);
}
SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return fileattrs_get(sigar, file, fileattrs, 1);
}
static __inline int file_type(char *file)
{
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (!GetFileAttributesExA(file,
GetFileExInfoStandard,
&attrs))
{
return -1;
}
if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
return SIGAR_FILETYPE_LNK;
}
else if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
return SIGAR_FILETYPE_DIR;
}
else {
return SIGAR_FILETYPE_REG;
}
}
static int dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats,
int recurse)
{
int status;
char name[SIGAR_PATH_MAX+1];
int len = strlen(dir);
int max = sizeof(name)-len-1;
char *ptr = name;
WIN32_FIND_DATA data;
HANDLE handle;
DWORD error;
char delim;
if (file_type((char *)dir) != SIGAR_FILETYPE_DIR) {
return ERROR_NO_MORE_FILES;
}
strncpy(name, dir, sizeof(name));
ptr += len;
if (strchr(dir, '/')) {
delim = '/';
}
else {
delim = '\\';
}
if (name[len] != delim) {
*ptr++ = delim;
len++;
max--;
}
/* e.g. "C:\sigar\*" */
name[len] = '*';
name[len+1] = '\0';
handle = FindFirstFile(name, &data);
if (handle == INVALID_HANDLE_VALUE) {
return GetLastError();
}
do {
/* skip '.' and '..' */
if (IS_DOTDIR(data.cFileName)) {
continue;
}
dirstats->disk_usage +=
(data.nFileSizeHigh * (MAXDWORD+1)) +
data.nFileSizeLow;
/* e.g. "C:\sigar\lib" */
strncpy(ptr, data.cFileName, max);
ptr[max] = '\0';
switch (file_type(name)) {
case -1:
break;
case SIGAR_FILETYPE_REG:
++dirstats->files;
break;
case SIGAR_FILETYPE_DIR:
++dirstats->subdirs;
if (recurse) {
status =
dir_stat_get(sigar, name,
dirstats, recurse);
if (status != SIGAR_OK) {
DIR_STAT_WARN();
}
}
break;
case SIGAR_FILETYPE_LNK:
++dirstats->symlinks;
break;
case SIGAR_FILETYPE_CHR:
++dirstats->chrdevs;
break;
case SIGAR_FILETYPE_BLK:
++dirstats->blkdevs;
break;
case SIGAR_FILETYPE_SOCK:
++dirstats->sockets;
break;
default:
++dirstats->total;
}
} while (FindNextFile(handle, &data));
error = GetLastError();
FindClose(handle);
if (error != ERROR_NO_MORE_FILES) {
return error;
}
dirstats->total =
dirstats->files +
dirstats->subdirs +
dirstats->symlinks +
dirstats->chrdevs +
dirstats->blkdevs +
dirstats->sockets;
return SIGAR_OK;
}
#else
#include
#include
#include
#include
static sigar_file_type_e filetype_from_mode(mode_t mode)
{
sigar_file_type_e type;
switch (mode & S_IFMT) {
case S_IFREG:
type = SIGAR_FILETYPE_REG; break;
case S_IFDIR:
type = SIGAR_FILETYPE_DIR; break;
case S_IFLNK:
type = SIGAR_FILETYPE_LNK; break;
case S_IFCHR:
type = SIGAR_FILETYPE_CHR; break;
case S_IFBLK:
type = SIGAR_FILETYPE_BLK; break;
#if defined(S_IFFIFO)
case S_IFFIFO:
type = SIGAR_FILETYPE_PIPE; break;
#endif
#if !defined(BEOS) && defined(S_IFSOCK)
case S_IFSOCK:
type = SIGAR_FILETYPE_SOCK; break;
#endif
default:
/* Work around missing S_IFxxx values above
* for Linux et al.
*/
#if !defined(S_IFFIFO) && defined(S_ISFIFO)
if (S_ISFIFO(mode)) {
type = SIGAR_FILETYPE_PIPE;
} else
#endif
#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK)
if (S_ISSOCK(mode)) {
type = SIGAR_FILETYPE_SOCK;
} else
#endif
type = SIGAR_FILETYPE_UNKFILE;
}
return type;
}
static sigar_uint64_t sigar_unix_mode2perms(mode_t mode)
{
sigar_uint64_t perms = 0;
if (mode & S_IRUSR)
perms |= SIGAR_UREAD;
if (mode & S_IWUSR)
perms |= SIGAR_UWRITE;
if (mode & S_IXUSR)
perms |= SIGAR_UEXECUTE;
if (mode & S_IRGRP)
perms |= SIGAR_GREAD;
if (mode & S_IWGRP)
perms |= SIGAR_GWRITE;
if (mode & S_IXGRP)
perms |= SIGAR_GEXECUTE;
if (mode & S_IROTH)
perms |= SIGAR_WREAD;
if (mode & S_IWOTH)
perms |= SIGAR_WWRITE;
if (mode & S_IXOTH)
perms |= SIGAR_WEXECUTE;
return perms;
}
static void copy_stat_info(sigar_file_attrs_t *fileattrs,
struct stat *info)
{
fileattrs->permissions = sigar_unix_mode2perms(info->st_mode);
fileattrs->type = filetype_from_mode(info->st_mode);
fileattrs->uid = info->st_uid;
fileattrs->gid = info->st_gid;
fileattrs->size = info->st_size;
fileattrs->inode = info->st_ino;
fileattrs->device = info->st_dev;
fileattrs->nlink = info->st_nlink;
fileattrs->atime = info->st_atime;
fileattrs->mtime = info->st_mtime;
fileattrs->ctime = info->st_ctime;
fileattrs->atime *= 1000;
fileattrs->mtime *= 1000;
fileattrs->ctime *= 1000;
}
int sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
struct stat info;
if (stat(file, &info) == 0) {
copy_stat_info(fileattrs, &info);
return SIGAR_OK;
}
else {
return errno;
}
}
int sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
struct stat info;
if (lstat(file, &info) == 0) {
copy_stat_info(fileattrs, &info);
return SIGAR_OK;
}
else {
return errno;
}
}
static int dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats,
int recurse)
{
int status;
char name[SIGAR_PATH_MAX+1];
int len = strlen(dir);
int max = sizeof(name)-len-1;
char *ptr = name;
DIR *dirp = opendir(dir);
struct dirent *ent;
struct stat info;
#ifdef HAVE_READDIR_R
struct dirent dbuf;
#endif
if (!dirp) {
return errno;
}
strncpy(name, dir, sizeof(name));
ptr += len;
if (name[len] != '/') {
*ptr++ = '/';
len++;
max--;
}
#ifdef HAVE_READDIR_R
while (readdir_r(dirp, &dbuf, &ent) == 0) {
if (ent == NULL) {
break;
}
#else
while ((ent = readdir(dirp))) {
#endif
/* skip '.' and '..' */
if (IS_DOTDIR(ent->d_name)) {
continue;
}
strncpy(ptr, ent->d_name, max);
ptr[max] = '\0';
if (lstat(name, &info) != 0) {
continue;
}
dirstats->disk_usage += info.st_size;
switch (filetype_from_mode(info.st_mode)) {
case SIGAR_FILETYPE_REG:
++dirstats->files;
break;
case SIGAR_FILETYPE_DIR:
++dirstats->subdirs;
if (recurse) {
status =
dir_stat_get(sigar, name,
dirstats, recurse);
if (status != SIGAR_OK) {
DIR_STAT_WARN();
}
}
break;
case SIGAR_FILETYPE_LNK:
++dirstats->symlinks;
break;
case SIGAR_FILETYPE_CHR:
++dirstats->chrdevs;
break;
case SIGAR_FILETYPE_BLK:
++dirstats->blkdevs;
break;
case SIGAR_FILETYPE_SOCK:
++dirstats->sockets;
break;
default:
++dirstats->total;
}
}
dirstats->total =
dirstats->files +
dirstats->subdirs +
dirstats->symlinks +
dirstats->chrdevs +
dirstats->blkdevs +
dirstats->sockets;
closedir(dirp);
return SIGAR_OK;
}
#endif
SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats)
{
SIGAR_ZERO(dirstats);
return dir_stat_get(sigar, dir, dirstats, 0);
}
SIGAR_DECLARE(int) sigar_dir_usage_get(sigar_t *sigar,
const char *dir,
sigar_dir_usage_t *dirusage)
{
SIGAR_ZERO(dirusage);
return dir_stat_get(sigar, dir, dirusage, 1);
}