1/*
2 * Copyright (c) 2004-2005, 2007-2008 Hyperic, Inc.
3 * Copyright (c) 2009 SpringSource, Inc.
4 * Copyright (c) 2010 VMware, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19/* ====================================================================
20 * The Apache Software License, Version 1.1
21 *
22 * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
23 * reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 *
29 * 1. Redistributions of source code must retain the above copyright
30 *    notice, this list of conditions and the following disclaimer.
31 *
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in
34 *    the documentation and/or other materials provided with the
35 *    distribution.
36 *
37 * 3. The end-user documentation included with the redistribution,
38 *    if any, must include the following acknowledgment:
39 *       "This product includes software developed by the
40 *        Apache Software Foundation (http://www.apache.org/)."
41 *    Alternately, this acknowledgment may appear in the software itself,
42 *    if and wherever such third-party acknowledgments normally appear.
43 *
44 * 4. The names "Apache" and "Apache Software Foundation" must
45 *    not be used to endorse or promote products derived from this
46 *    software without prior written permission. For written
47 *    permission, please contact apache@apache.org.
48 *
49 * 5. Products derived from this software may not be called "Apache",
50 *    nor may "Apache" appear in their name, without prior written
51 *    permission of the Apache Software Foundation.
52 *
53 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
54 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
55 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
56 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
57 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
58 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
59 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
60 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
62 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
63 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 * ====================================================================
66 *
67 * This software consists of voluntary contributions made by many
68 * individuals on behalf of the Apache Software Foundation.  For more
69 * information on the Apache Software Foundation, please see
70 * <http://www.apache.org/>.
71 */
72
73#ifndef WIN32
74#  ifdef _AIX
75#    define _LARGE_FILES
76#  else
77#    define _FILE_OFFSET_BITS 64
78#    define _LARGEFILE64_SOURCE
79#  endif
80#endif
81
82#include "sigar.h"
83
84#ifndef WIN32
85#if defined(__FreeBSD__) || defined(__OpenBSD__)
86# include <sys/param.h>
87# include <sys/mount.h>
88#else
89# include <sys/statvfs.h>
90# define HAVE_STATVFS
91#endif
92#include <errno.h>
93
94#include "sigar_private.h"
95#include "sigar_util.h"
96
97#define SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) ((val * bsize) >> 1)
98
99int sigar_statvfs(sigar_t *sigar,
100                  const char *dirname,
101                  sigar_file_system_usage_t *fsusage)
102{
103    sigar_uint64_t val, bsize;
104#ifdef HAVE_STATVFS
105    struct statvfs buf;
106    int status =
107# if defined(__sun) && !defined(_LP64)
108        /* http://bugs.opensolaris.org/view_bug.do?bug_id=4462986 */
109        statvfs(dirname, (void *)&buf);
110# else
111        statvfs(dirname, &buf);
112# endif
113#else
114    struct statfs buf;
115    int status = statfs(dirname, &buf);
116#endif
117
118    if (status != 0) {
119        return errno;
120    }
121
122#ifdef HAVE_STATVFS
123    bsize = buf.f_frsize / 512;
124#else
125    bsize = buf.f_bsize / 512;
126#endif
127    val = buf.f_blocks;
128    fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
129    val = buf.f_bfree;
130    fsusage->free  = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
131    val = buf.f_bavail;
132    fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
133    fsusage->used  = fsusage->total - fsusage->free;
134    fsusage->files = buf.f_files;
135    fsusage->free_files = buf.f_ffree;
136
137    return SIGAR_OK;
138}
139#endif
140
141/*
142 * whittled down version of apr/file_info/{unix,win32}/filestat.c
143 * to fillin sigar_fileattrs_t
144 */
145#include "sigar_fileinfo.h"
146#include "sigar_log.h"
147
148#ifndef SIGAR_ZERO
149#define SIGAR_ZERO(s) \
150    memset(s, '\0', sizeof(*(s)))
151#endif
152
153#ifdef WIN32
154#include <windows.h>
155sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft);
156#else
157#include <string.h>
158#endif
159
160static const char* types[] = {
161    "none",
162    "regular",
163    "directory",
164    "character device",
165    "block device",
166    "pipe",
167    "symbolic link",
168    "socket",
169    "unknown"
170};
171
172SIGAR_DECLARE(const char *)
173sigar_file_attrs_type_string_get(sigar_file_type_e type)
174{
175    if ((type < SIGAR_FILETYPE_NOFILE) ||
176        (type > SIGAR_FILETYPE_UNKFILE))
177    {
178        type = SIGAR_FILETYPE_UNKFILE;
179    }
180
181    return types[type];
182}
183
184static const sigar_uint64_t perm_modes[] = {
185    SIGAR_UREAD, SIGAR_UWRITE, SIGAR_UEXECUTE,
186    SIGAR_GREAD, SIGAR_GWRITE, SIGAR_GEXECUTE,
187    SIGAR_WREAD, SIGAR_WWRITE, SIGAR_WEXECUTE
188};
189
190static const char perm_chars[] = "rwx";
191
192SIGAR_DECLARE(char *)
193sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions,
194                                        char *str)
195{
196    char *ptr = str;
197    int i=0, j=0;
198
199    for (i=0; i<9; i+=3) {
200        for (j=0; j<3; j++) {
201            if (permissions & perm_modes[i+j]) {
202                *ptr = perm_chars[j];
203            }
204            else {
205                *ptr = '-';
206            }
207            ptr++;
208        }
209    }
210
211    *ptr = '\0';
212    return str;
213}
214
215static const int perm_int[] = {
216    400, 200, 100,
217     40,  20,  10,
218      4,   2,   1
219};
220
221SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions)
222{
223    int i=0;
224    int perms = 0;
225
226    /* no doubt there is some fancy bitshifting
227     * to convert, but this works fine.
228     */
229    for (i=0; i<9; i++) {
230        if (permissions & perm_modes[i]) {
231            perms += perm_int[i];
232        }
233    }
234
235    return perms;
236}
237
238#define IS_DOTDIR(dir) \
239    ((dir[0] == '.') && (!dir[1] || ((dir[1] == '.') && !dir[2])))
240
241#define DIR_STAT_WARN() \
242    sigar_log_printf(sigar, SIGAR_LOG_WARN, \
243                     "dir_stat: cannot stat `%s': %s", \
244                     name, \
245                     sigar_strerror(sigar, status))
246
247#if defined(NETWARE)
248
249int sigar_dir_stat_get(sigar_t *sigar,
250                       const char *dir,
251                       sigar_dir_stat_t *dirstats)
252{
253    return SIGAR_ENOTIMPL;
254}
255
256int sigar_file_attrs_get(sigar_t *sigar,
257                         const char *file,
258                         sigar_file_attrs_t *fileattrs)
259{
260    return SIGAR_ENOTIMPL;
261}
262
263int sigar_link_attrs_get(sigar_t *sigar,
264                         const char *file,
265                         sigar_file_attrs_t *fileattrs)
266{
267    return SIGAR_ENOTIMPL;
268}
269
270#elif defined(WIN32)
271
272#include <accctrl.h>
273#include <aclapi.h>
274
275static void fillin_fileattrs(sigar_file_attrs_t *finfo,
276                             WIN32_FILE_ATTRIBUTE_DATA *wininfo,
277                             int linkinfo)
278{
279    DWORD *sizes = &wininfo->nFileSizeHigh;
280
281    finfo->atime = sigar_FileTimeToTime(&wininfo->ftLastAccessTime) / 1000;
282    finfo->ctime = sigar_FileTimeToTime(&wininfo->ftCreationTime) / 1000;
283    finfo->mtime = sigar_FileTimeToTime(&wininfo->ftLastWriteTime) / 1000;
284
285    finfo->size =
286        (sigar_uint64_t)sizes[1] | ((sigar_uint64_t)sizes[0] << 32);
287
288    if (linkinfo &&
289        (wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
290        finfo->type = SIGAR_FILETYPE_LNK;
291    }
292    else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
293        finfo->type = SIGAR_FILETYPE_DIR;
294    }
295    else {
296        finfo->type = SIGAR_FILETYPE_REG;
297    }
298}
299
300static sigar_uint64_t convert_perms(ACCESS_MASK acc, sigar_uint64_t scope)
301{
302    sigar_uint64_t perms = 0;
303    if (acc & FILE_EXECUTE) {
304        perms |= SIGAR_WEXECUTE;
305    }
306    if (acc & FILE_WRITE_DATA) {
307        perms |= SIGAR_WWRITE;
308    }
309    if (acc & FILE_READ_DATA) {
310        perms |= SIGAR_WREAD;
311    }
312
313    return (perms << scope);
314}
315
316static int get_security_info(sigar_t *sigar,
317                             const char *file,
318                             sigar_file_attrs_t *fileattrs)
319{
320    DWORD retval;
321    PSID user = NULL, group = NULL, world = NULL;
322    PACL dacl = NULL;
323    PSECURITY_DESCRIPTOR pdesc = NULL;
324    SECURITY_INFORMATION sinfo =
325        OWNER_SECURITY_INFORMATION |
326        GROUP_SECURITY_INFORMATION |
327        DACL_SECURITY_INFORMATION;
328    TRUSTEE ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
329    ACCESS_MASK acc;
330    SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY;
331
332    retval = GetNamedSecurityInfo((char *)file,
333                                  SE_FILE_OBJECT,
334                                  sinfo,
335                                  &user,
336                                  &group,
337                                  &dacl,
338                                  NULL,
339                                  &pdesc);
340
341    if (retval != ERROR_SUCCESS) {
342        return retval;
343    }
344
345    if (!AllocateAndInitializeSid(&auth, 1, SECURITY_WORLD_RID,
346                                  0, 0, 0, 0, 0, 0, 0, &world))
347    {
348        world = NULL;
349    }
350
351    ident.TrusteeType = TRUSTEE_IS_USER;
352    ident.ptstrName = user;
353    if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
354        fileattrs->permissions |= convert_perms(acc, 8);
355    }
356
357    ident.TrusteeType = TRUSTEE_IS_GROUP;
358    ident.ptstrName = group;
359    if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
360        fileattrs->permissions |= convert_perms(acc, 4);
361    }
362
363    if (world) {
364        ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
365        ident.ptstrName = world;
366        if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
367            fileattrs->permissions |= convert_perms(acc, 0);
368        }
369    }
370
371    if (world) {
372        FreeSid(world);
373    }
374
375    LocalFree(pdesc);
376
377    return SIGAR_OK;
378}
379
380static int fileattrs_get(sigar_t *sigar,
381                         const char *file,
382                         sigar_file_attrs_t *fileattrs,
383                         int linkinfo)
384{
385    BY_HANDLE_FILE_INFORMATION info;
386    WIN32_FILE_ATTRIBUTE_DATA attrs;
387    HANDLE handle;
388    DWORD flags;
389
390    SIGAR_ZERO(fileattrs);
391
392    if (!GetFileAttributesExA(file,
393                              GetFileExInfoStandard,
394                              &attrs))
395    {
396        return GetLastError();
397    }
398
399    fillin_fileattrs(fileattrs, &attrs, linkinfo);
400
401    flags = fileattrs->type == SIGAR_FILETYPE_DIR ?
402        FILE_FLAG_BACKUP_SEMANTICS :
403        FILE_ATTRIBUTE_NORMAL;
404
405    /**
406     * We need to set dwDesiredAccess to 0 to work in cases where GENERIC_READ can fail.
407     *
408     * see: http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
409     */
410    handle = CreateFile(file,
411                        0,
412                        0,
413                        NULL,
414                        OPEN_EXISTING,
415                        flags,
416                        NULL);
417
418    if (handle != INVALID_HANDLE_VALUE) {
419        if (GetFileInformationByHandle(handle, &info)) {
420            fileattrs->inode =
421                (long long)info.nFileIndexLow |
422                ((long long)info.nFileIndexHigh << 32);
423            fileattrs->device = info.dwVolumeSerialNumber;
424            fileattrs->nlink  = info.nNumberOfLinks;
425        }
426        CloseHandle(handle);
427    }
428
429    get_security_info(sigar, file, fileattrs);
430
431    return SIGAR_OK;
432}
433
434SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar,
435                                        const char *file,
436                                        sigar_file_attrs_t *fileattrs)
437{
438    return fileattrs_get(sigar, file, fileattrs, 0);
439}
440
441SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar,
442                                        const char *file,
443                                        sigar_file_attrs_t *fileattrs)
444{
445    return fileattrs_get(sigar, file, fileattrs, 1);
446}
447
448static __inline int file_type(char *file)
449{
450    WIN32_FILE_ATTRIBUTE_DATA attrs;
451
452    if (!GetFileAttributesExA(file,
453                              GetFileExInfoStandard,
454                              &attrs))
455    {
456        return -1;
457    }
458
459    if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
460        return SIGAR_FILETYPE_LNK;
461    }
462    else if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
463        return SIGAR_FILETYPE_DIR;
464    }
465    else {
466        return SIGAR_FILETYPE_REG;
467    }
468}
469
470static int dir_stat_get(sigar_t *sigar,
471                        const char *dir,
472                        sigar_dir_stat_t *dirstats,
473                        int recurse)
474{
475    int status;
476    char name[SIGAR_PATH_MAX+1];
477    int len = strlen(dir);
478    int max = sizeof(name)-len-1;
479    char *ptr = name;
480    WIN32_FIND_DATA data;
481    HANDLE handle;
482    DWORD error;
483    char delim;
484
485    if (file_type((char *)dir) != SIGAR_FILETYPE_DIR) {
486        return ERROR_NO_MORE_FILES;
487    }
488
489    strncpy(name, dir, sizeof(name));
490    ptr += len;
491    if (strchr(dir, '/')) {
492        delim = '/';
493    }
494    else {
495        delim = '\\';
496    }
497    if (name[len] != delim) {
498        *ptr++ = delim;
499        len++;
500        max--;
501    }
502
503    /* e.g. "C:\sigar\*" */
504    name[len] = '*';
505    name[len+1] = '\0';
506
507    handle = FindFirstFile(name, &data);
508    if (handle == INVALID_HANDLE_VALUE) {
509        return GetLastError();
510    }
511
512    do {
513        /* skip '.' and '..' */
514        if (IS_DOTDIR(data.cFileName)) {
515            continue;
516        }
517
518        dirstats->disk_usage +=
519            (data.nFileSizeHigh * (MAXDWORD+1)) +
520            data.nFileSizeLow;
521
522        /* e.g. "C:\sigar\lib" */
523        strncpy(ptr, data.cFileName, max);
524        ptr[max] = '\0';
525
526        switch (file_type(name)) {
527          case -1:
528            break;
529          case SIGAR_FILETYPE_REG:
530            ++dirstats->files;
531            break;
532          case SIGAR_FILETYPE_DIR:
533            ++dirstats->subdirs;
534            if (recurse) {
535                status =
536                    dir_stat_get(sigar, name,
537                                 dirstats, recurse);
538                if (status != SIGAR_OK) {
539                    DIR_STAT_WARN();
540                }
541            }
542            break;
543          case SIGAR_FILETYPE_LNK:
544            ++dirstats->symlinks;
545            break;
546          case SIGAR_FILETYPE_CHR:
547            ++dirstats->chrdevs;
548            break;
549          case SIGAR_FILETYPE_BLK:
550            ++dirstats->blkdevs;
551            break;
552          case SIGAR_FILETYPE_SOCK:
553            ++dirstats->sockets;
554            break;
555          default:
556            ++dirstats->total;
557        }
558    } while (FindNextFile(handle, &data));
559
560    error = GetLastError();
561
562    FindClose(handle);
563
564    if (error != ERROR_NO_MORE_FILES) {
565        return error;
566    }
567
568    dirstats->total =
569        dirstats->files +
570        dirstats->subdirs +
571        dirstats->symlinks +
572        dirstats->chrdevs +
573        dirstats->blkdevs +
574        dirstats->sockets;
575
576    return SIGAR_OK;
577}
578
579#else
580
581#include <dirent.h>
582#include <errno.h>
583#include <sys/stat.h>
584#include <sys/types.h>
585
586static sigar_file_type_e filetype_from_mode(mode_t mode)
587{
588    sigar_file_type_e type;
589
590    switch (mode & S_IFMT) {
591    case S_IFREG:
592        type = SIGAR_FILETYPE_REG;  break;
593    case S_IFDIR:
594        type = SIGAR_FILETYPE_DIR;  break;
595    case S_IFLNK:
596        type = SIGAR_FILETYPE_LNK;  break;
597    case S_IFCHR:
598        type = SIGAR_FILETYPE_CHR;  break;
599    case S_IFBLK:
600        type = SIGAR_FILETYPE_BLK;  break;
601#if defined(S_IFFIFO)
602    case S_IFFIFO:
603        type = SIGAR_FILETYPE_PIPE; break;
604#endif
605#if !defined(BEOS) && defined(S_IFSOCK)
606    case S_IFSOCK:
607        type = SIGAR_FILETYPE_SOCK; break;
608#endif
609
610    default:
611	/* Work around missing S_IFxxx values above
612         * for Linux et al.
613         */
614#if !defined(S_IFFIFO) && defined(S_ISFIFO)
615    	if (S_ISFIFO(mode)) {
616            type = SIGAR_FILETYPE_PIPE;
617	} else
618#endif
619#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK)
620    	if (S_ISSOCK(mode)) {
621            type = SIGAR_FILETYPE_SOCK;
622	} else
623#endif
624        type = SIGAR_FILETYPE_UNKFILE;
625    }
626    return type;
627}
628
629static sigar_uint64_t sigar_unix_mode2perms(mode_t mode)
630{
631    sigar_uint64_t perms = 0;
632
633    if (mode & S_IRUSR)
634        perms |= SIGAR_UREAD;
635    if (mode & S_IWUSR)
636        perms |= SIGAR_UWRITE;
637    if (mode & S_IXUSR)
638        perms |= SIGAR_UEXECUTE;
639
640    if (mode & S_IRGRP)
641        perms |= SIGAR_GREAD;
642    if (mode & S_IWGRP)
643        perms |= SIGAR_GWRITE;
644    if (mode & S_IXGRP)
645        perms |= SIGAR_GEXECUTE;
646
647    if (mode & S_IROTH)
648        perms |= SIGAR_WREAD;
649    if (mode & S_IWOTH)
650        perms |= SIGAR_WWRITE;
651    if (mode & S_IXOTH)
652        perms |= SIGAR_WEXECUTE;
653
654    return perms;
655}
656
657static void copy_stat_info(sigar_file_attrs_t *fileattrs,
658                           struct stat *info)
659{
660    fileattrs->permissions = sigar_unix_mode2perms(info->st_mode);
661    fileattrs->type        = filetype_from_mode(info->st_mode);
662    fileattrs->uid         = info->st_uid;
663    fileattrs->gid         = info->st_gid;
664    fileattrs->size        = info->st_size;
665    fileattrs->inode       = info->st_ino;
666    fileattrs->device      = info->st_dev;
667    fileattrs->nlink       = info->st_nlink;
668    fileattrs->atime       = info->st_atime;
669    fileattrs->mtime       = info->st_mtime;
670    fileattrs->ctime       = info->st_ctime;
671    fileattrs->atime *= 1000;
672    fileattrs->mtime *= 1000;
673    fileattrs->ctime *= 1000;
674}
675
676int sigar_file_attrs_get(sigar_t *sigar,
677                         const char *file,
678                         sigar_file_attrs_t *fileattrs)
679{
680    struct stat info;
681
682    if (stat(file, &info) == 0) {
683        copy_stat_info(fileattrs, &info);
684        return SIGAR_OK;
685    }
686    else {
687        return errno;
688    }
689}
690
691int sigar_link_attrs_get(sigar_t *sigar,
692                         const char *file,
693                         sigar_file_attrs_t *fileattrs)
694{
695    struct stat info;
696
697    if (lstat(file, &info) == 0) {
698        copy_stat_info(fileattrs, &info);
699        return SIGAR_OK;
700    }
701    else {
702        return errno;
703    }
704}
705
706static int dir_stat_get(sigar_t *sigar,
707                        const char *dir,
708                        sigar_dir_stat_t *dirstats,
709                        int recurse)
710{
711    int status;
712    char name[SIGAR_PATH_MAX+1];
713    int len = strlen(dir);
714    int max = sizeof(name)-len-1;
715    char *ptr = name;
716    DIR *dirp = opendir(dir);
717    struct dirent *ent;
718    struct stat info;
719#ifdef HAVE_READDIR_R
720    struct dirent dbuf;
721#endif
722
723    if (!dirp) {
724        return errno;
725    }
726
727    strncpy(name, dir, sizeof(name));
728    ptr += len;
729    if (name[len] != '/') {
730        *ptr++ = '/';
731        len++;
732        max--;
733    }
734
735#ifdef HAVE_READDIR_R
736    while (readdir_r(dirp, &dbuf, &ent) == 0) {
737        if (ent == NULL) {
738            break;
739        }
740#else
741    while ((ent = readdir(dirp))) {
742#endif
743        /* skip '.' and '..' */
744        if (IS_DOTDIR(ent->d_name)) {
745            continue;
746        }
747
748        strncpy(ptr, ent->d_name, max);
749        ptr[max] = '\0';
750
751        if (lstat(name, &info) != 0) {
752            continue;
753        }
754
755        dirstats->disk_usage += info.st_size;
756
757        switch (filetype_from_mode(info.st_mode)) {
758          case SIGAR_FILETYPE_REG:
759            ++dirstats->files;
760            break;
761          case SIGAR_FILETYPE_DIR:
762            ++dirstats->subdirs;
763            if (recurse) {
764                status =
765                    dir_stat_get(sigar, name,
766                                 dirstats, recurse);
767                if (status != SIGAR_OK) {
768                    DIR_STAT_WARN();
769                }
770            }
771            break;
772          case SIGAR_FILETYPE_LNK:
773            ++dirstats->symlinks;
774            break;
775          case SIGAR_FILETYPE_CHR:
776            ++dirstats->chrdevs;
777            break;
778          case SIGAR_FILETYPE_BLK:
779            ++dirstats->blkdevs;
780            break;
781          case SIGAR_FILETYPE_SOCK:
782            ++dirstats->sockets;
783            break;
784          default:
785            ++dirstats->total;
786        }
787    }
788
789    dirstats->total =
790        dirstats->files +
791        dirstats->subdirs +
792        dirstats->symlinks +
793        dirstats->chrdevs +
794        dirstats->blkdevs +
795        dirstats->sockets;
796
797    closedir(dirp);
798
799    return SIGAR_OK;
800}
801
802#endif
803
804SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar,
805                                      const char *dir,
806                                      sigar_dir_stat_t *dirstats)
807{
808    SIGAR_ZERO(dirstats);
809    return dir_stat_get(sigar, dir, dirstats, 0);
810}
811
812SIGAR_DECLARE(int) sigar_dir_usage_get(sigar_t *sigar,
813                                       const char *dir,
814                                       sigar_dir_usage_t *dirusage)
815{
816    SIGAR_ZERO(dirusage);
817    return dir_stat_get(sigar, dir, dirusage, 1);
818}
819