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 
sigar_statvfs(sigar_t *sigar, const char *dirname, sigar_file_system_usage_t *fsusage)99 int 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>
155 sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft);
156 #else
157 #include <string.h>
158 #endif
159 
160 static 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 
172 SIGAR_DECLARE(const char *)
sigar_file_attrs_type_string_get(sigar_file_type_e type)173 sigar_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 
184 static 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 
190 static const char perm_chars[] = "rwx";
191 
192 SIGAR_DECLARE(char *)
sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions, char *str)193 sigar_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 
215 static const int perm_int[] = {
216     400, 200, 100,
217      40,  20,  10,
218       4,   2,   1
219 };
220 
sigar_file_attrs_mode_get(sigar_uint64_t permissions)221 SIGAR_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 
sigar_dir_stat_get(sigar_t *sigar, const char *dir, sigar_dir_stat_t *dirstats)249 int sigar_dir_stat_get(sigar_t *sigar,
250                        const char *dir,
251                        sigar_dir_stat_t *dirstats)
252 {
253     return SIGAR_ENOTIMPL;
254 }
255 
sigar_file_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)256 int sigar_file_attrs_get(sigar_t *sigar,
257                          const char *file,
258                          sigar_file_attrs_t *fileattrs)
259 {
260     return SIGAR_ENOTIMPL;
261 }
262 
sigar_link_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)263 int 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 
fillin_fileattrs(sigar_file_attrs_t *finfo, WIN32_FILE_ATTRIBUTE_DATA *wininfo, int linkinfo)275 static 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 
convert_perms(ACCESS_MASK acc, sigar_uint64_t scope)300 static 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 
get_security_info(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)316 static 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 
fileattrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs, int linkinfo)380 static 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 
sigar_file_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)434 SIGAR_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 
sigar_link_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)441 SIGAR_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 
file_type(char *file)448 static __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 
dir_stat_get(sigar_t *sigar, const char *dir, sigar_dir_stat_t *dirstats, int recurse)470 static 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 
filetype_from_mode(mode_t mode)586 static 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 
sigar_unix_mode2perms(mode_t mode)629 static 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 
copy_stat_info(sigar_file_attrs_t *fileattrs, struct stat *info)657 static 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 
sigar_file_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)676 int 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 
sigar_link_attrs_get(sigar_t *sigar, const char *file, sigar_file_attrs_t *fileattrs)691 int 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 
dir_stat_get(sigar_t *sigar, const char *dir, sigar_dir_stat_t *dirstats, int recurse)706 static 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 
804 SIGAR_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 
812 SIGAR_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