Add procfd.c with gpfs_set_winattrs_path()
[slow/toolbox.git] / enum.c
1 /*
2    Directory enumeration check
3    Copyright (C) Ralph Boehme <slowl@samba.org>         2016
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define _GNU_SOURCE
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <dirent.h>
28 #include <sys/types.h>
29
30 #define ERR_USAGE       1
31 #define ERR_SYSCALL     2
32 #define ERR_CALL        3
33 #define ERR_FATAL       4
34
35 #define ERROR(err, fmt, ...)                                            \
36         do {                                                            \
37                 do_log(__FILE__, __LINE__, __FUNCTION__, (fmt), __VA_ARGS__); \
38                 exit(err);                                              \
39         } while (0)
40
41 #define LOG(fmt, ...)                                                   \
42         do {                                                            \
43                 do_log(__FILE__, __LINE__, __FUNCTION__, (fmt), __VA_ARGS__); \
44         } while (0)
45
46 #define FREE(p)                         \
47         do {                            \
48                 free(p);                \
49                 p = 0;                  \
50         } while (0)
51
52 static void do_log(const char *file, int line, const char *func, const char *fmt, ...)
53 {
54         char *message;
55         va_list args;
56         int len;
57
58         va_start(args, fmt);
59         len = vasprintf(&message, fmt, args);
60         va_end(args);
61         if (len == -1) {
62                 exit(ERR_FATAL);
63         }
64
65         printf("%s:%d(%s): %s\n", file, line, func, message);
66         FREE(message);
67 }
68
69 /*
70  * Read n entries from a directory
71  *
72  * If check_name is not NULL, the first entry's name is compared
73  * against it.
74  *
75  * If plast_name is not NULL, the last name is returned *AND* the
76  * directory location is rewound to its position.
77  */
78 static bool enumdir_check(DIR *dir,
79                           bool print,
80                           int n,
81                           const char *check_name,
82                           char **plast_name)
83 {
84         struct dirent *de = NULL;
85         int i;
86         long prev_offset;
87
88         for (i = 0; i < n; i++) {
89                 prev_offset = telldir(dir);
90                 if (prev_offset == -1) {
91                         ERROR(ERR_SYSCALL, "telldir error %s", strerror(errno));
92                 }
93
94                 de = readdir(dir);
95                 if (de == NULL) {
96                         ERROR(ERR_SYSCALL, "readir error %s", strerror(errno));
97                 }
98
99                 if (i == 0 && check_name != NULL) {
100                         if (strcmp(check_name, de->d_name) != 0) {
101                                 ERROR(ERR_CALL, "expected %s got %s", check_name, de->d_name);
102                         }
103                 }
104                 if (print) {
105                         printf("%s\n", de->d_name);
106                 }
107         }
108
109         if (plast_name != NULL) {
110                 seekdir(dir, prev_offset);
111                 *plast_name = strdup(de->d_name);
112                 if (*plast_name == NULL) {
113                         ERROR(ERR_CALL, "strdup error %s", strerror(errno));
114                 }
115         }
116
117         return true;
118 }
119
120 /*
121  * This is the number of entries that shall be fetched via readir() in
122  * a loop. It is modelled after the behavour of a SMB client doing a
123  * SMB2 query directory request with a buffer size of 64k. The plus
124  * one is the entry that will rewind with seekdir().
125  */
126 #define ENUM_NUM_1 372 + 1
127 #define ENUM_NUM_2 372 + 1
128 #define ENUM_NUM_3 9            /* 372 + 372 + 9 = 753 */
129
130 int main(int argc, char **argv)
131 {
132         bool ok;
133         DIR *dir = NULL;
134         const char *dirpath = NULL;
135         char *last_name = NULL;
136
137         if (argc != 2) {
138                 ERROR(ERR_USAGE, "usage: %s PATH", argv[0]);
139         }
140         dirpath = argv[1];
141
142         dir = opendir(dirpath);
143         if (dir == NULL) {
144                 ERROR(ERR_SYSCALL, "opendir %s error %s", dirpath, strerror(errno));
145         }
146
147         ok = enumdir_check(dir, true, ENUM_NUM_1, NULL, &last_name);
148         if (!ok) {
149                 ERROR(ERR_CALL, "enum_check: %s", strerror(errno));
150         }
151
152         printf("Last entry: %s, rewound to it...\n", last_name);
153
154         ok = enumdir_check(dir, true, ENUM_NUM_2,
155                            last_name, &last_name);
156         if (!ok) {
157                 ERROR(ERR_CALL, "enum_check: %s", strerror(errno));
158         }
159
160         printf("Last entry: %s, rewound to it...\n", last_name);
161
162         ok = enumdir_check(dir, true, ENUM_NUM_3,
163                            last_name, &last_name);
164         if (!ok) {
165                 ERROR(ERR_CALL, "enum_check: %s", strerror(errno));
166         }
167
168         FREE(last_name);
169         (void)closedir(dir);
170
171         return 0;
172 }