• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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