libarmutils  1.4
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups
file_utils.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2010 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 16656 $
16 * $Author: ermold $
17 * $Date: 2013-01-24 01:51:31 +0000 (Thu, 24 Jan 2013) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file file_utils.c
26  * File Utilities
27  */
28 
29 #include "armutils.h"
30 
31 #if !defined(__GNUC__) || defined HAVE_MD5_H
32 #include <md5.h>
33 #else
34 #include <sasl/md5global.h>
35 #include <sasl/md5.h>
36 #define MD5Init _sasl_MD5Init
37 #define MD5Update _sasl_MD5Update
38 #define MD5Final _sasl_MD5Final
39 #endif
40 
41 /*******************************************************************************
42  * Private Functions
43  */
44 /** @privatesection */
45 
46 /*******************************************************************************
47  * Public Functions
48  */
49 /** @publicsection */
50 
51 /**
52  * Copy a file.
53  *
54  * This function will prepend the destination file with a '.' and add a '.lck'
55  * extenstion to it while the file is being copied. When the copy has been
56  * completed successfuly the rename function is used to remove the '.' prefix
57  * and '.lck' extension.
58  *
59  * Error messages from this function are sent to the message handler
60  * (see msngr_init_log() and msngr_init_mail()).
61  *
62  * @param src_file - path to the source file
63  * @param dest_file - path to the destination file
64  * @param flags - control flags
65  *
66  * <b>Control Flags:</b>
67  *
68  * - FC_CHECK_MD5 = Use MD5 validation.
69  *
70  * @return
71  * - 1 if the file copy was successful
72  * - 0 if an error occurred
73  */
75  const char *src_file,
76  const char *dest_file,
77  int flags)
78 {
79  size_t dest_len;
80  char tmp_file[PATH_MAX];
81  char *chrp;
82  int src_fd;
83  int tmp_fd;
84  struct stat src_stats;
85  char *buf;
86  size_t buf_size;
87  ssize_t nread;
88  ssize_t nwritten;
89  char src_md5[33];
90  char tmp_md5[33];
91 
92  /* Create the tmp file name */
93 
94  dest_len = strlen(dest_file);
95  if ((dest_len == 0) || (dest_len > PATH_MAX - 6)) {
96 
98  "Could not copy file:\n"
99  " -> from: %s\n"
100  " -> to: %s\n"
101  " -> invalid destination file length: %d\n",
102  src_file, dest_file, dest_len);
103 
104  return(0);
105  }
106 
107  strcpy(tmp_file, dest_file);
108 
109  for (chrp = tmp_file + dest_len; ; chrp--) {
110 
111  *(chrp+1) = *chrp;
112 
113  if (chrp == tmp_file) break;
114  if (*(chrp-1) == '/') break;
115  }
116  *chrp = '.';
117 
118  strcat(tmp_file, ".lck");
119 
120  if (flags & FC_CHECK_MD5) {
121 
122  /* Get the src file MD5 */
123 
124  if (!file_get_md5(src_file, src_md5)) {
125 
127  "Could not copy file:\n"
128  " -> from: %s\n"
129  " -> to: %s\n"
130  " -> could not get source file MD5\n",
131  src_file, dest_file);
132 
133  return(0);
134  }
135  }
136 
137  /* Open the src file for reading */
138 
139  src_fd = open(src_file, O_RDONLY);
140  if (src_fd < 0) {
141 
143  "Could not copy file:\n"
144  " -> from: %s\n"
145  " -> to: %s\n"
146  " -> src file open error: %s\n",
147  src_file, dest_file, strerror(errno));
148 
149  return(0);
150  }
151 
152  /* Get the src file stats */
153 
154  if (fstat(src_fd, &src_stats) < 0) {
155 
157  "Could not copy file:\n"
158  " -> from: %s\n"
159  " -> to: %s\n"
160  " -> src file stat error: %s\n",
161  src_file, dest_file, strerror(errno));
162 
163  close(src_fd);
164  return(0);
165  }
166 
167  /* Open the tmp file for writing */
168 
169  tmp_fd = open(tmp_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
170  if (tmp_fd < 0) {
171 
173  "Could not copy file:\n"
174  " -> from: %s\n"
175  " -> to: %s\n"
176  " -> tmp file open error: %s\n",
177  src_file, tmp_file, strerror(errno));
178 
179  close(src_fd);
180  return(0);
181  }
182 
183  /* Copy the contents of the src file to the tmp file */
184 
185  buf_size = getpagesize();
186  buf = (char *)malloc(buf_size * sizeof(char));
187 
188  while ((nread = read(src_fd, buf, buf_size)) > 0) {
189 
190  nwritten = write(tmp_fd, buf, nread);
191  if (nwritten != nread) {
192  if (nwritten < 0) {
193 
195  "Could not copy file:\n"
196  " -> from: %s\n"
197  " -> to: %s\n"
198  " -> write error: %s\n",
199  src_file, tmp_file, strerror(errno));
200  }
201  else {
202 
204  "Could not copy file:\n"
205  " -> from: %s\n"
206  " -> to: %s\n"
207  " -> bytes written (%d) does not match bytes read (%d)\n",
208  src_file, tmp_file, (int)nwritten, (int)nread);
209  }
210 
211  free(buf);
212  close(src_fd);
213  close(tmp_fd);
214  unlink(tmp_file);
215  return(0);
216  }
217  }
218 
219  if (nread < 0) {
220 
222  "Could not copy file:\n"
223  " -> from: %s\n"
224  " -> to: %s\n"
225  " -> read error: %s\n",
226  src_file, tmp_file, strerror(errno));
227 
228  free(buf);
229  close(src_fd);
230  close(tmp_fd);
231  unlink(tmp_file);
232  return(0);
233  }
234 
235  free(buf);
236  close(src_fd);
237  close(tmp_fd);
238 
239  /* Set the tmp file access permissions */
240 
241  chmod(tmp_file, src_stats.st_mode & 07777);
242 
243  if (flags & FC_CHECK_MD5) {
244 
245  /* Get the tmp file MD5 */
246 
247  if (!file_get_md5(tmp_file, tmp_md5)) {
248 
250  "Could not copy file:\n"
251  " -> from: %s\n"
252  " -> to: %s\n"
253  " -> could not get destination file MD5\n",
254  src_file, tmp_file);
255 
256  unlink(tmp_file);
257  return(0);
258  }
259 
260  /* Check MD5s */
261 
262  if (strcmp(src_md5, tmp_md5) != 0) {
263 
265  "Could not copy file:\n"
266  " -> from: %s\n"
267  " -> to: %s\n"
268  " -> source and destination files have different MD5s\n",
269  src_file, tmp_file);
270 
271  unlink(tmp_file);
272  return(0);
273  }
274  }
275 
276  /* Rename the tmp file to the correct destination name */
277 
278  if (rename(tmp_file, dest_file) < 0) {
279 
281  "Could not copy file:\n"
282  " -> from: %s\n"
283  " -> to: %s\n"
284  " -> tmp file rename error: %s\n",
285  src_file, dest_file, strerror(errno));
286 
287  unlink(tmp_file);
288  return(0);
289  }
290 
291  return(1);
292 }
293 
294 /**
295  * Check if a file exists.
296  *
297  * @param file - path to the file
298  *
299  * @return
300  * - 1 if the file exists
301  * - 0 if the file does not exist
302  */
303 int file_exists(const char *file)
304 {
305  if (access(file, F_OK) == 0) {
306  return(1);
307  }
308 
309  return(0);
310 }
311 
312 /**
313  * Get the MD5 of a file.
314  *
315  * The hexdigest buffer must be large enough to hold 33 characters
316  * (32 for the hex value plus one for the string terminator).
317  *
318  * Error messages from this function are sent to the message handler
319  * (see msngr_init_log() and msngr_init_mail()).
320  *
321  * @param file - path to the file
322  * @param hexdigest - buffer to store the MD5 hexdigext value
323  *
324  * @return
325  * - pointer to the hexdigest buffer
326  * - NULL if an error occurred
327  */
328 char *file_get_md5(const char *file, char *hexdigest)
329 {
330  int fd;
331  char *buf;
332  size_t buf_size;
333  ssize_t nread;
334  MD5_CTX md5_context;
335  unsigned char md5_digest[16];
336  int i;
337 
338  fd = open(file, O_RDONLY);
339  if (fd < 0) {
340 
342  "Could not get MD5 for file: %s\n"
343  " -> open error: %s\n", file, strerror(errno));
344 
345  return((char *)NULL);
346  }
347 
348  MD5Init(&md5_context);
349 
350  buf_size = getpagesize();
351  buf = (char *)malloc(buf_size * sizeof(char));
352 
353  while ((nread = read(fd, buf, buf_size)) > 0) {
354  MD5Update(&md5_context, buf, nread);
355  }
356 
357  if (nread == -1) {
358 
360  "Could not get MD5 for file: %s\n"
361  " -> read error: %s\n", file, strerror(errno));
362 
363  free(buf);
364  close(fd);
365  return((char *)NULL);
366  }
367 
368  free(buf);
369  close(fd);
370 
371  MD5Final(md5_digest, &md5_context);
372 
373  for (i = 0; i < 16; i++) {
374  sprintf(hexdigest + 2 * i, "%02x", md5_digest[i]);
375  }
376 
377  return(hexdigest);
378 }
379 
380 /**
381  * Move a file.
382  *
383  * This function will first attempt to simply rename the file.
384  * If the rename fails because the file is being moved across
385  * file systems, the file_copy() function will be used and the
386  * source file deleted.
387  *
388  * Error messages from this function are sent to the message handler
389  * (see msngr_init_log() and msngr_init_mail()).
390  *
391  * @param src_file - path to the old file name
392  * @param dest_file - path to the new file name
393  * @param flags - control flags
394  *
395  * <b>Control Flags:</b>
396  *
397  * - FC_CHECK_MD5 = Use MD5 validation. This flag will be ignored
398  * unless it is necessary to copy and delete the
399  * file in order to move it.
400  *
401  * @return
402  * - 1 if the file was moved
403  * - 0 if an error occurred
404  */
406  const char *src_file,
407  const char *dest_file,
408  int flags)
409 {
410  struct stat old_stats;
411  struct utimbuf ut;
412 
413  /* First try using rename */
414 
415  if (rename(src_file, dest_file) == 0) {
416  return(1);
417  }
418 
419  /* Check if we are moving the file across file systems */
420 
421  if (errno != EXDEV) {
422 
424  "Could not move file:\n"
425  " -> from: %s\n"
426  " -> to: %s\n"
427  " -> rename error: %s\n",
428  src_file, dest_file, strerror(errno));
429 
430  return(0);
431  }
432 
433  /* We are moving the file across file systems */
434 
435  /* Get the old file stats */
436 
437  if (stat(src_file, &old_stats) < 0) {
438 
440  "Could not move file:\n"
441  " -> from: %s\n"
442  " -> to: %s\n"
443  " -> stat error: %s\n",
444  src_file, dest_file, strerror(errno));
445 
446  return(0);
447  }
448 
449  /* Copy the old file to the new file */
450 
451  if (!file_copy(src_file, dest_file, flags)) {
452  return(0);
453  }
454 
455  /* Set the new file access and modification times */
456 
457  ut.actime = old_stats.st_atime;
458  ut.modtime = old_stats.st_mtime;
459  utime(dest_file, &ut);
460 
461  /* Unlink the old file */
462 
463  if (unlink(src_file) < 0) {
464 
466  "Could not unlink file: %s\n"
467  " -> %s\n", src_file, strerror(errno));
468 
469  return(0);
470  }
471 
472  return(1);
473 }
474 
475 /**
476  * Create a memory map of a file for reading.
477  *
478  * Error messages from this function are sent to the message handler
479  * (see msngr_init_log() and msngr_init_mail()).
480  *
481  * @param file - path to the file
482  * @param map_size - returns the size of the memory mapped file
483  *
484  * @return
485  * - address of the memory map
486  * - (void *)-1 if an error occurred
487  */
488 void *file_mmap(const char *file, size_t *map_size)
489 {
490  int fd;
491  struct stat file_stats;
492  void *map_addr;
493 
494  fd = open(file, O_RDONLY);
495  if (fd < 0) {
496 
498  "Could not create memory map for file: %s\n"
499  " -> open error: %s\n", file, strerror(errno));
500 
501  return(MAP_FAILED);
502  }
503 
504  if (fstat(fd, &file_stats) == -1) {
505 
507  "Could not create memory map for file: %s\n"
508  " -> fstat error: %s\n", file, strerror(errno));
509 
510  close(fd);
511  return(MAP_FAILED);
512  }
513 
514  *map_size = file_stats.st_size;
515 
516  map_addr = mmap((void *)NULL, *map_size, PROT_READ, MAP_SHARED, fd, 0);
517  if (map_addr == MAP_FAILED) {
518 
520  "Could not create memory map for file: %s\n"
521  " -> mmap error: %s\n", file, strerror(errno));
522 
523  close(fd);
524  return(MAP_FAILED);
525  }
526 
527  close(fd);
528 
529  return(map_addr);
530 }
531 
532 /**
533  * Remove a memory map created to read a file.
534  *
535  * Error messages from this function are sent to the message handler
536  * (see msngr_init_log() and msngr_init_mail()).
537  *
538  * @param map_addr - address of the memory map
539  * @param map_size - size of the memory mapped file
540  *
541  * @return
542  * - 1 if successful
543  * - 0 if an error occurred
544  */
545 int file_munmap(void *map_addr, size_t map_size)
546 {
547  if (munmap(map_addr, map_size) == -1) {
548 
550  "Could not remove memory map\n"
551  " -> munmap error: %s\n", strerror(errno));
552 
553  return(0);
554  }
555 
556  return(1);
557 }