1 /*
2 * File functions for the CUPS scheduler.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2014 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include "cupsd.h"
17 #include <cups/dir.h>
18 #include <fnmatch.h>
19 #ifdef HAVE_REMOVEFILE
20 # include <removefile.h>
21 #else
22 static int overwrite_data(int fd, const char *buffer, int bufsize,
23 int filesize);
24 #endif /* HAVE_REMOVEFILE */
25
26
27 /*
28 * 'cupsdCleanFiles()' - Clean out old files.
29 */
30
31 void
cupsdCleanFiles(const char * path,const char * pattern)32 cupsdCleanFiles(const char *path, /* I - Directory to clean */
33 const char *pattern) /* I - Filename pattern or NULL */
34 {
35 cups_dir_t *dir; /* Directory */
36 cups_dentry_t *dent; /* Directory entry */
37 char filename[1024]; /* Filename */
38 int status; /* Status from unlink/rmdir */
39
40
41 cupsdLogMessage(CUPSD_LOG_DEBUG,
42 "cupsdCleanFiles(path=\"%s\", pattern=\"%s\")", path,
43 pattern ? pattern : "(null)");
44
45 if ((dir = cupsDirOpen(path)) == NULL)
46 {
47 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open directory \"%s\" - %s",
48 path, strerror(errno));
49 return;
50 }
51
52 cupsdLogMessage(CUPSD_LOG_INFO, "Cleaning out old files in \"%s\".", path);
53
54 while ((dent = cupsDirRead(dir)) != NULL)
55 {
56 if (pattern && fnmatch(pattern, dent->filename, 0))
57 continue;
58
59 snprintf(filename, sizeof(filename), "%s/%s", path, dent->filename);
60
61 if (S_ISDIR(dent->fileinfo.st_mode))
62 {
63 cupsdCleanFiles(filename, pattern);
64
65 status = rmdir(filename);
66 }
67 else
68 status = cupsdUnlinkOrRemoveFile(filename);
69
70 if (status)
71 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove \"%s\" - %s", filename,
72 strerror(errno));
73 }
74
75 cupsDirClose(dir);
76 }
77
78
79 /*
80 * 'cupsdCloseCreatedConfFile()' - Close a created configuration file and move
81 * into place.
82 */
83
84 int /* O - 0 on success, -1 on error */
cupsdCloseCreatedConfFile(cups_file_t * fp,const char * filename)85 cupsdCloseCreatedConfFile(
86 cups_file_t *fp, /* I - File to close */
87 const char *filename) /* I - Filename */
88 {
89 char newfile[1024], /* filename.N */
90 oldfile[1024]; /* filename.O */
91
92
93 /*
94 * Synchronize changes to disk if SyncOnClose is enabled.
95 */
96
97 if (SyncOnClose)
98 {
99 if (cupsFileFlush(fp))
100 {
101 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to write changes to \"%s\": %s",
102 filename, strerror(errno));
103 cupsFileClose(fp);
104 return (-1);
105 }
106
107 if (fsync(cupsFileNumber(fp)))
108 {
109 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to sync changes to \"%s\": %s",
110 filename, strerror(errno));
111 cupsFileClose(fp);
112 return (-1);
113 }
114 }
115
116 /*
117 * First close the file...
118 */
119
120 if (cupsFileClose(fp))
121 return (-1);
122
123 /*
124 * Then remove "filename.O", rename "filename" to "filename.O", and rename
125 * "filename.N" to "filename".
126 */
127
128 snprintf(newfile, sizeof(newfile), "%s.N", filename);
129 snprintf(oldfile, sizeof(oldfile), "%s.O", filename);
130
131 if ((cupsdUnlinkOrRemoveFile(oldfile) && errno != ENOENT) ||
132 (rename(filename, oldfile) && errno != ENOENT) ||
133 rename(newfile, filename))
134 {
135 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to finalize \"%s\": %s",
136 filename, strerror(errno));
137 return (-1);
138 }
139
140 return (0);
141 }
142
143
144 /*
145 * 'cupsdClosePipe()' - Close a pipe as necessary.
146 */
147
148 void
cupsdClosePipe(int * fds)149 cupsdClosePipe(int *fds) /* I - Pipe file descriptors (2) */
150 {
151 /*
152 * Close file descriptors as needed...
153 */
154
155 if (fds[0] >= 0)
156 {
157 close(fds[0]);
158 fds[0] = -1;
159 }
160
161 if (fds[1] >= 0)
162 {
163 close(fds[1]);
164 fds[1] = -1;
165 }
166 }
167
168
169 /*
170 * 'cupsdCreateConfFile()' - Create a configuration file safely.
171 */
172
173 cups_file_t * /* O - File pointer */
cupsdCreateConfFile(const char * filename,mode_t mode)174 cupsdCreateConfFile(
175 const char *filename, /* I - Filename */
176 mode_t mode) /* I - Permissions */
177 {
178 cups_file_t *fp; /* File pointer */
179 char newfile[1024]; /* filename.N */
180
181
182 snprintf(newfile, sizeof(newfile), "%s.N", filename);
183 if ((fp = cupsFileOpen(newfile, "w")) == NULL)
184 {
185 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\": %s", newfile,
186 strerror(errno));
187 }
188 else
189 {
190 if (!getuid() && fchown(cupsFileNumber(fp), getuid(), Group))
191 cupsdLogMessage(CUPSD_LOG_WARN, "Unable to change group for \"%s\": %s",
192 newfile, strerror(errno));
193
194 if (fchmod(cupsFileNumber(fp), mode))
195 cupsdLogMessage(CUPSD_LOG_WARN,
196 "Unable to change permissions for \"%s\": %s",
197 newfile, strerror(errno));
198 }
199
200 return (fp);
201 }
202
203
204 /*
205 * 'cupsdOpenConfFile()' - Open a configuration file.
206 *
207 * This function looks for "filename.O" if "filename" does not exist and does
208 * a rename as needed.
209 */
210
211 cups_file_t * /* O - File pointer */
cupsdOpenConfFile(const char * filename)212 cupsdOpenConfFile(const char *filename) /* I - Filename */
213 {
214 cups_file_t *fp; /* File pointer */
215
216
217 if ((fp = cupsFileOpen(filename, "r")) == NULL)
218 {
219 if (errno == ENOENT)
220 {
221 /*
222 * Try opening the backup file...
223 */
224
225 char oldfile[1024]; /* filename.O */
226
227 snprintf(oldfile, sizeof(oldfile), "%s.O", filename);
228 fp = cupsFileOpen(oldfile, "r");
229 }
230 else
231 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\": %s", filename,
232 strerror(errno));
233 }
234
235 return (fp);
236 }
237
238
239 /*
240 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
241 */
242
243 int /* O - 0 on success, -1 on error */
cupsdOpenPipe(int * fds)244 cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */
245 {
246 /*
247 * Create the pipe...
248 */
249
250 if (pipe(fds))
251 {
252 fds[0] = -1;
253 fds[1] = -1;
254
255 return (-1);
256 }
257
258 /*
259 * Set the "close on exec" flag on each end of the pipe...
260 */
261
262 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
263 {
264 close(fds[0]);
265 close(fds[1]);
266
267 fds[0] = -1;
268 fds[1] = -1;
269
270 return (-1);
271 }
272
273 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
274 {
275 close(fds[0]);
276 close(fds[1]);
277
278 fds[0] = -1;
279 fds[1] = -1;
280
281 return (-1);
282 }
283
284 /*
285 * Return 0 indicating success...
286 */
287
288 return (0);
289 }
290
291
292 /*
293 * 'cupsdRemoveFile()' - Remove a file securely.
294 */
295
296 int /* O - 0 on success, -1 on error */
cupsdRemoveFile(const char * filename)297 cupsdRemoveFile(const char *filename) /* I - File to remove */
298 {
299 #ifdef HAVE_REMOVEFILE
300 /*
301 * See if the file exists...
302 */
303
304 if (access(filename, 0))
305 return (0);
306
307 cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename);
308
309 /*
310 * Remove the file...
311 */
312
313 return (removefile(filename, NULL, REMOVEFILE_SECURE_1_PASS));
314
315 #else
316 int fd; /* File descriptor */
317 struct stat info; /* File information */
318 char buffer[512]; /* Data buffer */
319 size_t i; /* Looping var */
320
321
322 /*
323 * See if the file exists...
324 */
325
326 if (access(filename, 0))
327 return (0);
328
329 cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename);
330
331 /*
332 * First open the file for writing in exclusive mode.
333 */
334
335 if ((fd = open(filename, O_WRONLY | O_EXCL)) < 0)
336 return (-1);
337
338 /*
339 * Delete the file now - it will still be around as long as the file is
340 * open...
341 */
342
343 if (unlink(filename))
344 {
345 close(fd);
346 return (-1);
347 }
348
349 /*
350 * Then get the file size...
351 */
352
353 if (fstat(fd, &info))
354 {
355 close(fd);
356 return (-1);
357 }
358
359 /*
360 * Overwrite the file with random data.
361 */
362
363 CUPS_SRAND(time(NULL));
364
365 for (i = 0; i < sizeof(buffer); i ++)
366 buffer[i] = CUPS_RAND();
367 if (overwrite_data(fd, buffer, sizeof(buffer), (int)info.st_size))
368 {
369 close(fd);
370 return (-1);
371 }
372
373 /*
374 * Close the file, which will lead to the actual deletion, and return...
375 */
376
377 return (close(fd));
378 #endif /* HAVE_REMOVEFILE */
379 }
380
381
382 /*
383 * 'cupsdUnlinkOrRemoveFile()' - Unlink or securely remove a file depending
384 * on the configuration.
385 */
386
387 int /* O - 0 on success, -1 on error */
cupsdUnlinkOrRemoveFile(const char * filename)388 cupsdUnlinkOrRemoveFile(
389 const char *filename) /* I - Filename */
390 {
391 if (Classification)
392 return (cupsdRemoveFile(filename));
393 else
394 return (unlink(filename));
395 }
396
397
398 #ifndef HAVE_REMOVEFILE
399 /*
400 * 'overwrite_data()' - Overwrite the data in a file.
401 */
402
403 static int /* O - 0 on success, -1 on error */
overwrite_data(int fd,const char * buffer,int bufsize,int filesize)404 overwrite_data(int fd, /* I - File descriptor */
405 const char *buffer, /* I - Buffer to write */
406 int bufsize, /* I - Size of buffer */
407 int filesize) /* I - Size of file */
408 {
409 int bytes; /* Bytes to write/written */
410
411
412 /*
413 * Start at the beginning of the file...
414 */
415
416 if (lseek(fd, 0, SEEK_SET) < 0)
417 return (-1);
418
419 /*
420 * Fill the file with the provided data...
421 */
422
423 while (filesize > 0)
424 {
425 if (filesize > bufsize)
426 bytes = bufsize;
427 else
428 bytes = filesize;
429
430 if ((bytes = write(fd, buffer, (size_t)bytes)) < 0)
431 return (-1);
432
433 filesize -= bytes;
434 }
435
436 /*
437 * Force the changes to disk...
438 */
439
440 return (fsync(fd));
441 }
442 #endif /* HAVE_REMOVEFILE */
443