Line | Count | Source |
1 | | /* |
2 | | * Directory routines for CUPS. |
3 | | * |
4 | | * This set of APIs abstracts enumeration of directory entries. |
5 | | * |
6 | | * Copyright © 2022-2025 by OpenPrinting. |
7 | | * Copyright © 2007-2021 by Apple Inc. |
8 | | * Copyright © 1997-2005 by Easy Software Products, all rights reserved. |
9 | | * |
10 | | * Licensed under Apache License v2.0. See the file "LICENSE" for more |
11 | | * information. |
12 | | */ |
13 | | |
14 | | #include "cups-private.h" |
15 | | #include "string-private.h" |
16 | | #include "debug-internal.h" |
17 | | #include "dir.h" |
18 | | |
19 | | |
20 | | // |
21 | | // Common code... |
22 | | // |
23 | | |
24 | | bool // O - `true` on success, `false` on failure |
25 | | _cupsDirCreate(const char *path, // I - Directory path |
26 | | mode_t mode) // I - Permissions of final directory |
27 | 0 | { |
28 | 0 | bool ret = true; // Return value |
29 | 0 | char *copypath, // Copy of path |
30 | 0 | *ptr; // Pointer into path |
31 | | |
32 | | |
33 | | // Copy the path |
34 | 0 | if ((copypath = strdup(path)) == NULL) |
35 | 0 | return (false); |
36 | | |
37 | | // Create any intermediate paths as needed... |
38 | 0 | for (ptr = strchr(copypath + 1, '/'); ptr; ptr = strchr(ptr + 1, '/')) |
39 | 0 | { |
40 | | // Truncate path for the subdir and create it modulo the umask... |
41 | 0 | *ptr = '\0'; |
42 | 0 | if (mkdir(copypath, 0777) && errno != EEXIST) |
43 | 0 | { |
44 | 0 | ret = false; |
45 | 0 | break; |
46 | 0 | } |
47 | 0 | *ptr = '/'; |
48 | 0 | } |
49 | | |
50 | | // Free the copy of the path and then make the last component... |
51 | 0 | free(copypath); |
52 | 0 | if (ret && mkdir(path, mode) && errno != EEXIST) |
53 | 0 | ret = false; |
54 | |
|
55 | 0 | return (ret); |
56 | 0 | } |
57 | | |
58 | | |
59 | | /* |
60 | | * Windows implementation... |
61 | | */ |
62 | | |
63 | | #ifdef _WIN32 |
64 | | # include <windows.h> |
65 | | |
66 | | /* |
67 | | * Types and structures... |
68 | | */ |
69 | | |
70 | | struct _cups_dir_s /**** Directory data structure ****/ |
71 | | { |
72 | | char directory[1024]; /* Directory filename */ |
73 | | HANDLE dir; /* Directory handle */ |
74 | | cups_dentry_t entry; /* Directory entry */ |
75 | | }; |
76 | | |
77 | | |
78 | | /* |
79 | | * '_cups_dir_time()' - Convert a FILETIME value to a UNIX time value. |
80 | | */ |
81 | | |
82 | | time_t /* O - UNIX time */ |
83 | | _cups_dir_time(FILETIME ft) /* I - File time */ |
84 | | { |
85 | | ULONGLONG val; /* File time in 0.1 usecs */ |
86 | | |
87 | | |
88 | | /* |
89 | | * Convert file time (1/10 microseconds since Jan 1, 1601) to UNIX |
90 | | * time (seconds since Jan 1, 1970). There are 11,644,732,800 seconds |
91 | | * between them... |
92 | | */ |
93 | | |
94 | | val = ft.dwLowDateTime + ((ULONGLONG)ft.dwHighDateTime << 32); |
95 | | return ((time_t)(val / 10000000 - 11644732800)); |
96 | | } |
97 | | |
98 | | |
99 | | /* |
100 | | * 'cupsDirClose()' - Close a directory. |
101 | | * |
102 | | * @since CUPS 1.2@ |
103 | | */ |
104 | | |
105 | | void |
106 | | cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */ |
107 | | { |
108 | | /* |
109 | | * Range check input... |
110 | | */ |
111 | | |
112 | | if (!dp) |
113 | | return; |
114 | | |
115 | | /* |
116 | | * Close an open directory handle... |
117 | | */ |
118 | | |
119 | | if (dp->dir != INVALID_HANDLE_VALUE) |
120 | | FindClose(dp->dir); |
121 | | |
122 | | /* |
123 | | * Free memory used... |
124 | | */ |
125 | | |
126 | | free(dp); |
127 | | } |
128 | | |
129 | | |
130 | | /* |
131 | | * 'cupsDirOpen()' - Open a directory. |
132 | | * |
133 | | * @since CUPS 1.2@ |
134 | | */ |
135 | | |
136 | | cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */ |
137 | | cupsDirOpen(const char *directory) /* I - Directory name */ |
138 | | { |
139 | | cups_dir_t *dp; /* Directory */ |
140 | | |
141 | | |
142 | | /* |
143 | | * Range check input... |
144 | | */ |
145 | | |
146 | | if (!directory) |
147 | | return (NULL); |
148 | | |
149 | | /* |
150 | | * Allocate memory for the directory structure... |
151 | | */ |
152 | | |
153 | | dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t)); |
154 | | if (!dp) |
155 | | return (NULL); |
156 | | |
157 | | /* |
158 | | * Copy the directory name for later use... |
159 | | */ |
160 | | |
161 | | dp->dir = INVALID_HANDLE_VALUE; |
162 | | |
163 | | snprintf(dp->directory, sizeof(dp->directory), "%s\\*", directory); |
164 | | |
165 | | /* |
166 | | * Return the new directory structure... |
167 | | */ |
168 | | |
169 | | return (dp); |
170 | | } |
171 | | |
172 | | |
173 | | /* |
174 | | * 'cupsDirRead()' - Read the next directory entry. |
175 | | * |
176 | | * @since CUPS 1.2@ |
177 | | */ |
178 | | |
179 | | cups_dentry_t * /* O - Directory entry or @code NULL@ if there are no more */ |
180 | | cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */ |
181 | | { |
182 | | WIN32_FIND_DATAA entry; /* Directory entry data */ |
183 | | |
184 | | |
185 | | /* |
186 | | * Range check input... |
187 | | */ |
188 | | |
189 | | if (!dp) |
190 | | return (NULL); |
191 | | |
192 | | /* |
193 | | * See if we have already started finding files... |
194 | | */ |
195 | | |
196 | | if (dp->dir == INVALID_HANDLE_VALUE) |
197 | | { |
198 | | /* |
199 | | * No, find the first file... |
200 | | */ |
201 | | |
202 | | dp->dir = FindFirstFileA(dp->directory, &entry); |
203 | | if (dp->dir == INVALID_HANDLE_VALUE) |
204 | | return (NULL); |
205 | | } |
206 | | else if (!FindNextFileA(dp->dir, &entry)) |
207 | | return (NULL); |
208 | | |
209 | | /* |
210 | | * Loop until we have something other than "." or ".."... |
211 | | */ |
212 | | |
213 | | while (!strcmp(entry.cFileName, ".") || !strcmp(entry.cFileName, "..")) |
214 | | { |
215 | | if (!FindNextFileA(dp->dir, &entry)) |
216 | | return (NULL); |
217 | | } |
218 | | |
219 | | /* |
220 | | * Copy the name over and convert the file information... |
221 | | */ |
222 | | |
223 | | cupsCopyString(dp->entry.filename, entry.cFileName, sizeof(dp->entry.filename)); |
224 | | |
225 | | if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
226 | | dp->entry.fileinfo.st_mode = 0755 | S_IFDIR; |
227 | | else |
228 | | dp->entry.fileinfo.st_mode = 0644 | S_IFREG; |
229 | | |
230 | | dp->entry.fileinfo.st_atime = _cups_dir_time(entry.ftLastAccessTime); |
231 | | dp->entry.fileinfo.st_ctime = _cups_dir_time(entry.ftCreationTime); |
232 | | dp->entry.fileinfo.st_mtime = _cups_dir_time(entry.ftLastWriteTime); |
233 | | dp->entry.fileinfo.st_size = entry.nFileSizeLow + ((unsigned long long)entry.nFileSizeHigh << 32); |
234 | | |
235 | | /* |
236 | | * Return the entry... |
237 | | */ |
238 | | |
239 | | return (&(dp->entry)); |
240 | | } |
241 | | |
242 | | |
243 | | /* |
244 | | * 'cupsDirRewind()' - Rewind to the start of the directory. |
245 | | * |
246 | | * @since CUPS 1.2@ |
247 | | */ |
248 | | |
249 | | void |
250 | | cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */ |
251 | | { |
252 | | /* |
253 | | * Range check input... |
254 | | */ |
255 | | |
256 | | if (!dp) |
257 | | return; |
258 | | |
259 | | /* |
260 | | * Close an open directory handle... |
261 | | */ |
262 | | |
263 | | if (dp->dir != INVALID_HANDLE_VALUE) |
264 | | { |
265 | | FindClose(dp->dir); |
266 | | dp->dir = INVALID_HANDLE_VALUE; |
267 | | } |
268 | | } |
269 | | |
270 | | |
271 | | #else |
272 | | |
273 | | /* |
274 | | * POSIX implementation... |
275 | | */ |
276 | | |
277 | | # include <sys/types.h> |
278 | | # include <dirent.h> |
279 | | |
280 | | |
281 | | /* |
282 | | * Types and structures... |
283 | | */ |
284 | | |
285 | | struct _cups_dir_s /**** Directory data structure ****/ |
286 | | { |
287 | | char directory[1024]; /* Directory filename */ |
288 | | DIR *dir; /* Directory file */ |
289 | | cups_dentry_t entry; /* Directory entry */ |
290 | | }; |
291 | | |
292 | | |
293 | | /* |
294 | | * 'cupsDirClose()' - Close a directory. |
295 | | * |
296 | | * @since CUPS 1.2@ |
297 | | */ |
298 | | |
299 | | void |
300 | | cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */ |
301 | 0 | { |
302 | 0 | DEBUG_printf("cupsDirClose(dp=%p)", (void *)dp); |
303 | | |
304 | | /* |
305 | | * Range check input... |
306 | | */ |
307 | |
|
308 | 0 | if (!dp) |
309 | 0 | return; |
310 | | |
311 | | /* |
312 | | * Close the directory and free memory... |
313 | | */ |
314 | | |
315 | 0 | closedir(dp->dir); |
316 | 0 | free(dp); |
317 | 0 | } |
318 | | |
319 | | |
320 | | /* |
321 | | * 'cupsDirOpen()' - Open a directory. |
322 | | * |
323 | | * @since CUPS 1.2@ |
324 | | */ |
325 | | |
326 | | cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */ |
327 | | cupsDirOpen(const char *directory) /* I - Directory name */ |
328 | 0 | { |
329 | 0 | cups_dir_t *dp; /* Directory */ |
330 | | |
331 | |
|
332 | 0 | DEBUG_printf("cupsDirOpen(directory=\"%s\")", directory); |
333 | | |
334 | | /* |
335 | | * Range check input... |
336 | | */ |
337 | |
|
338 | 0 | if (!directory) |
339 | 0 | return (NULL); |
340 | | |
341 | | /* |
342 | | * Allocate memory for the directory structure... |
343 | | */ |
344 | | |
345 | 0 | dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t)); |
346 | 0 | if (!dp) |
347 | 0 | return (NULL); |
348 | | |
349 | | /* |
350 | | * Open the directory... |
351 | | */ |
352 | | |
353 | 0 | dp->dir = opendir(directory); |
354 | 0 | if (!dp->dir) |
355 | 0 | { |
356 | 0 | free(dp); |
357 | 0 | return (NULL); |
358 | 0 | } |
359 | | |
360 | | /* |
361 | | * Copy the directory name for later use... |
362 | | */ |
363 | | |
364 | 0 | cupsCopyString(dp->directory, directory, sizeof(dp->directory)); |
365 | | |
366 | | /* |
367 | | * Return the new directory structure... |
368 | | */ |
369 | |
|
370 | 0 | return (dp); |
371 | 0 | } |
372 | | |
373 | | |
374 | | /* |
375 | | * 'cupsDirRead()' - Read the next directory entry. |
376 | | * |
377 | | * @since CUPS 1.2@ |
378 | | */ |
379 | | |
380 | | cups_dentry_t * /* O - Directory entry or @code NULL@ when there are no more */ |
381 | | cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */ |
382 | 0 | { |
383 | 0 | struct dirent *entry; /* Pointer to entry */ |
384 | 0 | char filename[1024]; /* Full filename */ |
385 | | |
386 | |
|
387 | 0 | DEBUG_printf("2cupsDirRead(dp=%p)", (void *)dp); |
388 | | |
389 | | /* |
390 | | * Range check input... |
391 | | */ |
392 | |
|
393 | 0 | if (!dp) |
394 | 0 | return (NULL); |
395 | | |
396 | | /* |
397 | | * Try reading an entry that is not "." or ".."... |
398 | | */ |
399 | | |
400 | 0 | for (;;) |
401 | 0 | { |
402 | | /* |
403 | | * Read the next entry... |
404 | | */ |
405 | |
|
406 | 0 | if ((entry = readdir(dp->dir)) == NULL) |
407 | 0 | { |
408 | 0 | DEBUG_puts("3cupsDirRead: readdir() returned a NULL pointer!"); |
409 | 0 | return (NULL); |
410 | 0 | } |
411 | | |
412 | 0 | DEBUG_printf("4cupsDirRead: readdir() returned \"%s\"...", entry->d_name); |
413 | | |
414 | | /* |
415 | | * Skip "." and ".."... |
416 | | */ |
417 | |
|
418 | 0 | if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) |
419 | 0 | continue; |
420 | | |
421 | | /* |
422 | | * Copy the name over and get the file information... |
423 | | */ |
424 | | |
425 | 0 | cupsCopyString(dp->entry.filename, entry->d_name, sizeof(dp->entry.filename)); |
426 | |
|
427 | 0 | snprintf(filename, sizeof(filename), "%s/%s", dp->directory, entry->d_name); |
428 | |
|
429 | 0 | if (stat(filename, &(dp->entry.fileinfo))) |
430 | 0 | { |
431 | 0 | DEBUG_printf("3cupsDirRead: stat() failed for \"%s\" - %s...", filename, strerror(errno)); |
432 | 0 | continue; |
433 | 0 | } |
434 | | |
435 | | /* |
436 | | * Return the entry... |
437 | | */ |
438 | | |
439 | 0 | return (&(dp->entry)); |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | | |
444 | | /* |
445 | | * 'cupsDirRewind()' - Rewind to the start of the directory. |
446 | | * |
447 | | * @since CUPS 1.2@ |
448 | | */ |
449 | | |
450 | | void |
451 | | cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */ |
452 | 0 | { |
453 | 0 | DEBUG_printf("cupsDirRewind(dp=%p)", (void *)dp); |
454 | | |
455 | | /* |
456 | | * Range check input... |
457 | | */ |
458 | |
|
459 | 0 | if (!dp) |
460 | 0 | return; |
461 | | |
462 | | /* |
463 | | * Rewind the directory... |
464 | | */ |
465 | | |
466 | 0 | rewinddir(dp->dir); |
467 | 0 | } |
468 | | #endif /* _WIN32 */ |