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