NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
file.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <ctype.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <libgen.h>
35 #include <limits.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/file.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <utime.h>
44 #include "file.h"
45 #include "buffer.h"
46 #include "date.h"
47 #include "logging.h"
48 #include "memory.h"
49 #include "message.h"
50 #include "path.h"
51 #include "string2.h"
52 
53 char *C_Tmpdir;
54 
55 /* these characters must be escaped in regular expressions */
56 static const char rx_special_chars[] = "^.[$()|*+?{\\";
57 
58 const char filename_safe_chars[] =
59  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
60 
61 #define MAX_LOCK_ATTEMPTS 5
62 
63 /* This is defined in POSIX:2008 which isn't a build requirement */
64 #ifndef O_NOFOLLOW
65 #define O_NOFOLLOW 0
66 #endif
67 
77 static bool compare_stat(struct stat *osb, struct stat *nsb)
78 {
79  return (osb->st_dev == nsb->st_dev) && (osb->st_ino == nsb->st_ino) &&
80  (osb->st_rdev == nsb->st_rdev);
81 }
82 
91 static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
92 {
93  const char *basename = NULL;
94  int rc = 0;
95 
96  struct Buffer parent = mutt_buffer_make(PATH_MAX);
97  mutt_buffer_strcpy(&parent, NONULL(path));
98 
99  char *p = strrchr(parent.data, '/');
100  if (p)
101  {
102  *p = '\0';
103  basename = p + 1;
104  }
105  else
106  {
107  mutt_buffer_strcpy(&parent, ".");
108  basename = path;
109  }
110 
111  mutt_buffer_printf(newdir, "%s/%s", mutt_b2s(&parent), ".muttXXXXXX");
112  if (!mkdtemp(newdir->data))
113  {
114  mutt_debug(LL_DEBUG1, "mkdtemp() failed\n");
115  rc = -1;
116  goto cleanup;
117  }
118 
119  mutt_buffer_printf(newfile, "%s/%s", newdir->data, NONULL(basename));
120 
121 cleanup:
122  mutt_buffer_dealloc(&parent);
123  return rc;
124 }
125 
134 static int put_file_in_place(const char *path, const char *safe_file, const char *safe_dir)
135 {
136  int rc;
137 
138  rc = mutt_file_safe_rename(safe_file, path);
139  unlink(safe_file);
140  rmdir(safe_dir);
141  return rc;
142 }
143 
150 int mutt_file_fclose(FILE **fp)
151 {
152  if (!fp || !*fp)
153  return 0;
154 
155  int rc = fclose(*fp);
156  *fp = NULL;
157  return rc;
158 }
159 
166 int mutt_file_fsync_close(FILE **fp)
167 {
168  if (!fp || !*fp)
169  return 0;
170 
171  int rc = 0;
172 
173  if (fflush(*fp) || fsync(fileno(*fp)))
174  {
175  int save_errno = errno;
176  rc = -1;
177  mutt_file_fclose(fp);
178  errno = save_errno;
179  }
180  else
181  rc = mutt_file_fclose(fp);
182 
183  return rc;
184 }
185 
192 void mutt_file_unlink(const char *s)
193 {
194  if (!s)
195  return;
196 
197  struct stat sb;
198  /* Defend against symlink attacks */
199 
200  const bool is_regular_file = (lstat(s, &sb) == 0) && S_ISREG(sb.st_mode);
201  if (!is_regular_file)
202  return;
203 
204  const int fd = open(s, O_RDWR | O_NOFOLLOW);
205  if (fd < 0)
206  return;
207 
208  struct stat sb2;
209  if ((fstat(fd, &sb2) != 0) || !S_ISREG(sb2.st_mode) ||
210  (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
211  {
212  close(fd);
213  return;
214  }
215 
216  FILE *fp = fdopen(fd, "r+");
217  if (fp)
218  {
219  unlink(s);
220  char buf[2048] = { 0 };
221  while (sb.st_size > 0)
222  {
223  fwrite(buf, 1, MIN(sizeof(buf), sb.st_size), fp);
224  sb.st_size -= MIN(sizeof(buf), sb.st_size);
225  }
226  mutt_file_fclose(&fp);
227  }
228 }
229 
238 int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
239 {
240  if (!fp_in || !fp_out)
241  return -1;
242 
243  while (size > 0)
244  {
245  char buf[2048];
246  size_t chunk = (size > sizeof(buf)) ? sizeof(buf) : size;
247  chunk = fread(buf, 1, chunk, fp_in);
248  if (chunk < 1)
249  break;
250  if (fwrite(buf, 1, chunk, fp_out) != chunk)
251  return -1;
252 
253  size -= chunk;
254  }
255 
256  if (fflush(fp_out) != 0)
257  return -1;
258  return 0;
259 }
260 
268 int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
269 {
270  if (!fp_in || !fp_out)
271  return -1;
272 
273  size_t l;
274  char buf[1024];
275 
276  while ((l = fread(buf, 1, sizeof(buf), fp_in)) > 0)
277  {
278  if (fwrite(buf, 1, l, fp_out) != l)
279  return -1;
280  }
281 
282  if (fflush(fp_out) != 0)
283  return -1;
284  return 0;
285 }
286 
294 int mutt_file_symlink(const char *oldpath, const char *newpath)
295 {
296  struct stat osb, nsb;
297 
298  if (!oldpath || !newpath)
299  return -1;
300 
301  if ((unlink(newpath) == -1) && (errno != ENOENT))
302  return -1;
303 
304  if (oldpath[0] == '/')
305  {
306  if (symlink(oldpath, newpath) == -1)
307  return -1;
308  }
309  else
310  {
311  struct Buffer abs_oldpath = mutt_buffer_make(PATH_MAX);
312 
313  if (!mutt_path_getcwd(&abs_oldpath))
314  {
315  mutt_buffer_dealloc(&abs_oldpath);
316  return -1;
317  }
318 
319  mutt_buffer_addch(&abs_oldpath, '/');
320  mutt_buffer_addstr(&abs_oldpath, oldpath);
321  if (symlink(mutt_b2s(&abs_oldpath), newpath) == -1)
322  {
323  mutt_buffer_dealloc(&abs_oldpath);
324  return -1;
325  }
326 
327  mutt_buffer_dealloc(&abs_oldpath);
328  }
329 
330  if ((stat(oldpath, &osb) == -1) || (stat(newpath, &nsb) == -1) ||
331  !compare_stat(&osb, &nsb))
332  {
333  unlink(newpath);
334  return -1;
335  }
336 
337  return 0;
338 }
339 
349 int mutt_file_safe_rename(const char *src, const char *target)
350 {
351  struct stat ssb, tsb;
352  int link_errno;
353 
354  if (!src || !target)
355  return -1;
356 
357  if (link(src, target) != 0)
358  {
359  link_errno = errno;
360 
361  /* It is historically documented that link can return -1 if NFS
362  * dies after creating the link. In that case, we are supposed
363  * to use stat to check if the link was created.
364  *
365  * Derek Martin notes that some implementations of link() follow a
366  * source symlink. It might be more correct to use stat() on src.
367  * I am not doing so to minimize changes in behavior: the function
368  * used lstat() further below for 20 years without issue, and I
369  * believe was never intended to be used on a src symlink. */
370  if ((lstat(src, &ssb) == 0) && (lstat(target, &tsb) == 0) &&
371  (compare_stat(&ssb, &tsb) == 0))
372  {
373  mutt_debug(LL_DEBUG1, "link (%s, %s) reported failure: %s (%d) but actually succeeded\n",
374  src, target, strerror(errno), errno);
375  goto success;
376  }
377 
378  errno = link_errno;
379 
380  /* Coda does not allow cross-directory links, but tells
381  * us it's a cross-filesystem linking attempt.
382  *
383  * However, the Coda rename call is allegedly safe to use.
384  *
385  * With other file systems, rename should just fail when
386  * the files reside on different file systems, so it's safe
387  * to try it here. */
388  mutt_debug(LL_DEBUG1, "link (%s, %s) failed: %s (%d)\n", src, target,
389  strerror(errno), errno);
390 
391  /* FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
392  * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear. */
393  if ((errno == EXDEV) || (errno == ENOSYS) || errno == EPERM
394 #ifdef ENOTSUP
395  || errno == ENOTSUP
396 #endif
397 #ifdef EOPNOTSUPP
398  || errno == EOPNOTSUPP
399 #endif
400  )
401  {
402  mutt_debug(LL_DEBUG1, "trying rename\n");
403  if (rename(src, target) == -1)
404  {
405  mutt_debug(LL_DEBUG1, "rename (%s, %s) failed: %s (%d)\n", src, target,
406  strerror(errno), errno);
407  return -1;
408  }
409  mutt_debug(LL_DEBUG1, "rename succeeded\n");
410 
411  return 0;
412  }
413 
414  return -1;
415  }
416 
417  /* Remove the compare_stat() check, because it causes problems with maildir
418  * on filesystems that don't properly support hard links, such as sshfs. The
419  * filesystem creates the link, but the resulting file is given a different
420  * inode number by the sshfs layer. This results in an infinite loop
421  * creating links. */
422 #if 0
423  /* Stat both links and check if they are equal. */
424  if (lstat(src, &ssb) == -1)
425  {
426  mutt_debug(LL_DEBUG1, "#1 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
427  return -1;
428  }
429 
430  if (lstat(target, &tsb) == -1)
431  {
432  mutt_debug(LL_DEBUG1, "#2 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
433  return -1;
434  }
435 
436  /* pretend that the link failed because the target file did already exist. */
437 
438  if (!compare_stat(&ssb, &tsb))
439  {
440  mutt_debug(LL_DEBUG1, "stat blocks for %s and %s diverge; pretending EEXIST\n", src, target);
441  errno = EEXIST;
442  return -1;
443  }
444 #endif
445 
446 success:
447  /* Unlink the original link.
448  * Should we really ignore the return value here? XXX */
449  if (unlink(src) == -1)
450  {
451  mutt_debug(LL_DEBUG1, "unlink (%s) failed: %s (%d)\n", src, strerror(errno), errno);
452  }
453 
454  return 0;
455 }
456 
463 int mutt_file_rmtree(const char *path)
464 {
465  if (!path)
466  return -1;
467 
468  struct dirent *de = NULL;
469  struct stat statbuf;
470  int rc = 0;
471 
472  DIR *dirp = opendir(path);
473  if (!dirp)
474  {
475  mutt_debug(LL_DEBUG1, "error opening directory %s\n", path);
476  return -1;
477  }
478 
479  /* We avoid using the buffer pool for this function, because it
480  * invokes recursively to an unknown depth. */
481  struct Buffer cur = mutt_buffer_make(PATH_MAX);
482 
483  while ((de = readdir(dirp)))
484  {
485  if ((strcmp(".", de->d_name) == 0) || (strcmp("..", de->d_name) == 0))
486  continue;
487 
488  mutt_buffer_printf(&cur, "%s/%s", path, de->d_name);
489  /* XXX make nonrecursive version */
490 
491  if (stat(mutt_b2s(&cur), &statbuf) == -1)
492  {
493  rc = 1;
494  continue;
495  }
496 
497  if (S_ISDIR(statbuf.st_mode))
498  rc |= mutt_file_rmtree(mutt_b2s(&cur));
499  else
500  rc |= unlink(mutt_b2s(&cur));
501  }
502  closedir(dirp);
503 
504  rc |= rmdir(path);
505 
506  mutt_buffer_dealloc(&cur);
507  return rc;
508 }
509 
517 int mutt_file_open(const char *path, int flags)
518 {
519  if (!path)
520  return -1;
521 
522  struct stat osb, nsb;
523  int fd;
524  struct Buffer safe_file = mutt_buffer_make(0);
525  struct Buffer safe_dir = mutt_buffer_make(0);
526 
527  if (flags & O_EXCL)
528  {
529  mutt_buffer_alloc(&safe_file, PATH_MAX);
530  mutt_buffer_alloc(&safe_dir, PATH_MAX);
531 
532  if (mkwrapdir(path, &safe_file, &safe_dir) == -1)
533  {
534  fd = -1;
535  goto cleanup;
536  }
537 
538  fd = open(mutt_b2s(&safe_file), flags, 0600);
539  if (fd < 0)
540  {
541  rmdir(mutt_b2s(&safe_dir));
542  goto cleanup;
543  }
544 
545  /* NFS and I believe cygwin do not handle movement of open files well */
546  close(fd);
547  if (put_file_in_place(path, mutt_b2s(&safe_file), mutt_b2s(&safe_dir)) == -1)
548  {
549  fd = -1;
550  goto cleanup;
551  }
552  }
553 
554  fd = open(path, flags & ~O_EXCL, 0600);
555  if (fd < 0)
556  goto cleanup;
557 
558  /* make sure the file is not symlink */
559  if (((lstat(path, &osb) < 0) || (fstat(fd, &nsb) < 0)) || !compare_stat(&osb, &nsb))
560  {
561  close(fd);
562  fd = -1;
563  goto cleanup;
564  }
565 
566 cleanup:
567  mutt_buffer_dealloc(&safe_file);
568  mutt_buffer_dealloc(&safe_dir);
569 
570  return fd;
571 }
572 
583 FILE *mutt_file_fopen(const char *path, const char *mode)
584 {
585  if (!path || !mode)
586  return NULL;
587 
588  if (mode[0] == 'w')
589  {
590  int fd;
591  int flags = O_CREAT | O_EXCL | O_NOFOLLOW;
592 
593  if (mode[1] == '+')
594  flags |= O_RDWR;
595  else
596  flags |= O_WRONLY;
597 
598  fd = mutt_file_open(path, flags);
599  if (fd < 0)
600  return NULL;
601 
602  return fdopen(fd, mode);
603  }
604  else
605  return fopen(path, mode);
606 }
607 
613 void mutt_file_sanitize_filename(char *path, bool slash)
614 {
615  if (!path)
616  return;
617 
618  for (; *path; path++)
619  {
620  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
621  *path = '_';
622  }
623 }
624 
632 int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
633 {
634  if (!dest || !src)
635  return -1;
636 
637  mutt_buffer_reset(dest);
638  while (*src != '\0')
639  {
640  if (strchr(rx_special_chars, *src))
641  mutt_buffer_addch(dest, '\\');
642  mutt_buffer_addch(dest, *src++);
643  }
644 
645  return 0;
646 }
647 
662 char *mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
663 {
664  if (!size || !fp)
665  return NULL;
666 
667  size_t offset = 0;
668  char *ch = NULL;
669 
670  if (!line)
671  {
672  *size = 256;
673  line = mutt_mem_malloc(*size);
674  }
675 
676  while (true)
677  {
678  if (!fgets(line + offset, *size - offset, fp))
679  {
680  FREE(&line);
681  return NULL;
682  }
683  ch = strchr(line + offset, '\n');
684  if (ch)
685  {
686  if (line_num)
687  (*line_num)++;
688  if (flags & MUTT_EOL)
689  return line;
690  *ch = '\0';
691  if ((ch > line) && (*(ch - 1) == '\r'))
692  *--ch = '\0';
693  if (!(flags & MUTT_CONT) || (ch == line) || (*(ch - 1) != '\\'))
694  return line;
695  offset = ch - line - 1;
696  }
697  else
698  {
699  int c;
700  c = getc(fp); /* This is kind of a hack. We want to know if the
701  char at the current point in the input stream is EOF.
702  feof() will only tell us if we've already hit EOF, not
703  if the next character is EOF. So, we need to read in
704  the next character and manually check if it is EOF. */
705  if (c == EOF)
706  {
707  /* The last line of fp isn't \n terminated */
708  if (line_num)
709  (*line_num)++;
710  return line;
711  }
712  else
713  {
714  ungetc(c, fp); /* undo our damage */
715  /* There wasn't room for the line -- increase "line" */
716  offset = *size - 1; /* overwrite the terminating 0 */
717  *size += 256;
718  mutt_mem_realloc(&line, *size);
719  }
720  }
721  }
722 }
723 
742 bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, int flags)
743 {
744  if (!iter)
745  return false;
746 
747  char *p = mutt_file_read_line(iter->line, &iter->size, fp, &iter->line_num, flags);
748  if (!p)
749  return false;
750  iter->line = p;
751  return true;
752 }
753 
762 bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, int flags)
763 {
764  if (!func || !fp)
765  return false;
766 
767  struct MuttFileIter iter = { 0 };
768  while (mutt_file_iter_line(&iter, fp, flags))
769  {
770  if (!(*func)(iter.line, iter.line_num, user_data))
771  {
772  FREE(&iter.line);
773  return false;
774  }
775  }
776  return true;
777 }
778 
788 size_t mutt_file_quote_filename(const char *filename, char *buf, size_t buflen)
789 {
790  if (!buf)
791  return 0;
792 
793  if (!filename)
794  {
795  *buf = '\0';
796  return 0;
797  }
798 
799  size_t j = 0;
800 
801  /* leave some space for the trailing characters. */
802  buflen -= 6;
803 
804  buf[j++] = '\'';
805 
806  for (size_t i = 0; (j < buflen) && filename[i]; i++)
807  {
808  if ((filename[i] == '\'') || (filename[i] == '`'))
809  {
810  buf[j++] = '\'';
811  buf[j++] = '\\';
812  buf[j++] = filename[i];
813  buf[j++] = '\'';
814  }
815  else
816  buf[j++] = filename[i];
817  }
818 
819  buf[j++] = '\'';
820  buf[j] = '\0';
821 
822  return j;
823 }
824 
832 void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
833 {
834  if (!buf || !filename)
835  return;
836 
837  mutt_buffer_reset(buf);
838  if (add_outer)
839  mutt_buffer_addch(buf, '\'');
840 
841  for (; *filename != '\0'; filename++)
842  {
843  if ((*filename == '\'') || (*filename == '`'))
844  {
845  mutt_buffer_addch(buf, '\'');
846  mutt_buffer_addch(buf, '\\');
847  mutt_buffer_addch(buf, *filename);
848  mutt_buffer_addch(buf, '\'');
849  }
850  else
851  mutt_buffer_addch(buf, *filename);
852  }
853 
854  if (add_outer)
855  mutt_buffer_addch(buf, '\'');
856 }
857 
871 int mutt_file_mkdir(const char *path, mode_t mode)
872 {
873  if (!path || !*path)
874  {
875  errno = EINVAL;
876  return -1;
877  }
878 
879  errno = 0;
880  char tmp_path[PATH_MAX];
881  const size_t len = strlen(path);
882 
883  if (len >= sizeof(tmp_path))
884  {
885  errno = ENAMETOOLONG;
886  return -1;
887  }
888 
889  struct stat sb;
890  if ((stat(path, &sb) == 0) && S_ISDIR(sb.st_mode))
891  return 0;
892 
893  /* Create a mutable copy */
894  mutt_str_strfcpy(tmp_path, path, sizeof(tmp_path));
895 
896  for (char *p = tmp_path + 1; *p; p++)
897  {
898  if (*p != '/')
899  continue;
900 
901  /* Temporarily truncate the path */
902  *p = '\0';
903 
904  if ((mkdir(tmp_path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) && (errno != EEXIST))
905  return -1;
906 
907  *p = '/';
908  }
909 
910  if ((mkdir(tmp_path, mode) != 0) && (errno != EEXIST))
911  return -1;
912 
913  return 0;
914 }
915 
926 FILE *mutt_file_mkstemp_full(const char *file, int line, const char *func)
927 {
928  char name[PATH_MAX];
929 
930  int n = snprintf(name, sizeof(name), "%s/neomutt-XXXXXX", NONULL(C_Tmpdir));
931  if (n < 0)
932  return NULL;
933 
934  int fd = mkstemp(name);
935  if (fd == -1)
936  return NULL;
937 
938  FILE *fp = fdopen(fd, "w+");
939 
940  if ((unlink(name) != 0) && (errno != ENOENT))
941  {
942  mutt_file_fclose(&fp);
943  return NULL;
944  }
945 
946  MuttLogger(0, file, line, func, 1, "created temp file '%s'\n", name);
947  return fp;
948 }
949 
959 time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
960 {
961  if (!fp)
962  return -1;
963 
964  struct utimbuf utim;
965  struct stat st2;
966  time_t mtime;
967 
968  if (!st)
969  {
970  if (stat(fp, &st2) == -1)
971  return -1;
972  st = &st2;
973  }
974 
975  mtime = st->st_mtime;
976  if (mtime == mutt_date_epoch())
977  {
978  mtime -= 1;
979  utim.actime = mtime;
980  utim.modtime = mtime;
981  utime(fp, &utim);
982  }
983 
984  return mtime;
985 }
986 
992 void mutt_file_set_mtime(const char *from, const char *to)
993 {
994  if (!from || !to)
995  return;
996 
997  struct utimbuf utim;
998  struct stat st;
999 
1000  if (stat(from, &st) != -1)
1001  {
1002  utim.actime = st.st_mtime;
1003  utim.modtime = st.st_mtime;
1004  utime(to, &utim);
1005  }
1006 }
1007 
1016 {
1017 #ifdef HAVE_FUTIMENS
1018  struct timespec times[2] = { { 0, UTIME_NOW }, { 0, UTIME_OMIT } };
1019  futimens(fd, times);
1020 #endif
1021 }
1022 
1031 int mutt_file_chmod(const char *path, mode_t mode)
1032 {
1033  if (!path)
1034  return -1;
1035 
1036  return chmod(path, mode);
1037 }
1038 
1055 int mutt_file_chmod_add(const char *path, mode_t mode)
1056 {
1057  return mutt_file_chmod_add_stat(path, mode, NULL);
1058 }
1059 
1077 int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
1078 {
1079  if (!path)
1080  return -1;
1081 
1082  struct stat st2;
1083 
1084  if (!st)
1085  {
1086  if (stat(path, &st2) == -1)
1087  return -1;
1088  st = &st2;
1089  }
1090  return chmod(path, st->st_mode | mode);
1091 }
1092 
1109 int mutt_file_chmod_rm(const char *path, mode_t mode)
1110 {
1111  return mutt_file_chmod_rm_stat(path, mode, NULL);
1112 }
1113 
1131 int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
1132 {
1133  if (!path)
1134  return -1;
1135 
1136  struct stat st2;
1137 
1138  if (!st)
1139  {
1140  if (stat(path, &st2) == -1)
1141  return -1;
1142  st = &st2;
1143  }
1144  return chmod(path, st->st_mode & ~mode);
1145 }
1146 
1147 #if defined(USE_FCNTL)
1148 
1160 int mutt_file_lock(int fd, bool excl, bool timeout)
1161 {
1162  struct stat sb = { 0 }, prev_sb = { 0 };
1163  int count = 0;
1164  int attempt = 0;
1165 
1166  struct flock lck;
1167  memset(&lck, 0, sizeof(struct flock));
1168  lck.l_type = excl ? F_WRLCK : F_RDLCK;
1169  lck.l_whence = SEEK_SET;
1170 
1171  while (fcntl(fd, F_SETLK, &lck) == -1)
1172  {
1173  mutt_debug(LL_DEBUG1, "fcntl errno %d\n", errno);
1174  if ((errno != EAGAIN) && (errno != EACCES))
1175  {
1176  mutt_perror("fcntl");
1177  return -1;
1178  }
1179 
1180  if (fstat(fd, &sb) != 0)
1181  sb.st_size = 0;
1182 
1183  if (count == 0)
1184  prev_sb = sb;
1185 
1186  /* only unlock file if it is unchanged */
1187  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1188  {
1189  if (timeout)
1190  mutt_error(_("Timeout exceeded while attempting fcntl lock"));
1191  return -1;
1192  }
1193 
1194  prev_sb = sb;
1195 
1196  mutt_message(_("Waiting for fcntl lock... %d"), ++attempt);
1197  sleep(1);
1198  }
1199 
1200  return 0;
1201 }
1202 
1208 int mutt_file_unlock(int fd)
1209 {
1210  struct flock unlockit;
1211 
1212  memset(&unlockit, 0, sizeof(struct flock));
1213  unlockit.l_type = F_UNLCK;
1214  unlockit.l_whence = SEEK_SET;
1215  fcntl(fd, F_SETLK, &unlockit);
1216 
1217  return 0;
1218 }
1219 #elif defined(USE_FLOCK)
1220 
1232 int mutt_file_lock(int fd, bool excl, bool timeout)
1233 {
1234  struct stat sb = { 0 }, prev_sb = { 0 };
1235  int rc = 0;
1236  int count = 0;
1237  int attempt = 0;
1238 
1239  while (flock(fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
1240  {
1241  if (errno != EWOULDBLOCK)
1242  {
1243  mutt_perror("flock");
1244  rc = -1;
1245  break;
1246  }
1247 
1248  if (fstat(fd, &sb) != 0)
1249  sb.st_size = 0;
1250 
1251  if (count == 0)
1252  prev_sb = sb;
1253 
1254  /* only unlock file if it is unchanged */
1255  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1256  {
1257  if (timeout)
1258  mutt_error(_("Timeout exceeded while attempting flock lock"));
1259  rc = -1;
1260  break;
1261  }
1262 
1263  prev_sb = sb;
1264 
1265  mutt_message(_("Waiting for flock attempt... %d"), ++attempt);
1266  sleep(1);
1267  }
1268 
1269  /* release any other locks obtained in this routine */
1270  if (rc != 0)
1271  {
1272  flock(fd, LOCK_UN);
1273  }
1274 
1275  return rc;
1276 }
1277 
1283 int mutt_file_unlock(int fd)
1284 {
1285  flock(fd, LOCK_UN);
1286  return 0;
1287 }
1288 #else
1289 #error "You must select a locking mechanism via USE_FCNTL or USE_FLOCK"
1290 #endif
1291 
1296 void mutt_file_unlink_empty(const char *path)
1297 {
1298  if (!path)
1299  return;
1300 
1301  struct stat sb;
1302 
1303  int fd = open(path, O_RDWR);
1304  if (fd == -1)
1305  return;
1306 
1307  if (mutt_file_lock(fd, true, true) == -1)
1308  {
1309  close(fd);
1310  return;
1311  }
1312 
1313  if ((fstat(fd, &sb) == 0) && (sb.st_size == 0))
1314  unlink(path);
1315 
1316  mutt_file_unlock(fd);
1317  close(fd);
1318 }
1319 
1332 int mutt_file_rename(const char *oldfile, const char *newfile)
1333 {
1334  if (!oldfile || !newfile)
1335  return -1;
1336  if (access(oldfile, F_OK) != 0)
1337  return 1;
1338  if (access(newfile, F_OK) == 0)
1339  return 2;
1340 
1341  FILE *fp_old = fopen(oldfile, "r");
1342  if (!fp_old)
1343  return 3;
1344  FILE *fp_new = mutt_file_fopen(newfile, "w");
1345  if (!fp_new)
1346  {
1347  mutt_file_fclose(&fp_old);
1348  return 3;
1349  }
1350  mutt_file_copy_stream(fp_old, fp_new);
1351  mutt_file_fclose(&fp_new);
1352  mutt_file_fclose(&fp_old);
1353  mutt_file_unlink(oldfile);
1354  return 0;
1355 }
1356 
1367 char *mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
1368 {
1369  FILE *fp = mutt_file_fopen(file, "r");
1370  if (!fp)
1371  return NULL;
1372 
1373  buf = fgets(buf, buflen, fp);
1374  mutt_file_fclose(&fp);
1375 
1376  if (!buf)
1377  return NULL;
1378 
1379  SKIPWS(buf);
1380  char *start = buf;
1381 
1382  while ((*buf != '\0') && !isspace(*buf))
1383  buf++;
1384 
1385  *buf = '\0';
1386 
1387  return start;
1388 }
1389 
1397 int mutt_file_check_empty(const char *path)
1398 {
1399  if (!path)
1400  return -1;
1401 
1402  struct stat st;
1403  if (stat(path, &st) == -1)
1404  return -1;
1405 
1406  return st.st_size == 0;
1407 }
1408 
1417 void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
1418 {
1419  struct Buffer tmp = mutt_buffer_make(PATH_MAX);
1420 
1421  mutt_buffer_quote_filename(&tmp, src, true);
1422  mutt_file_expand_fmt(dest, fmt, mutt_b2s(&tmp));
1423  mutt_buffer_dealloc(&tmp);
1424 }
1425 
1432 void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
1433 {
1434  if (!dest || !fmt || !src)
1435  return;
1436 
1437  const char *p = NULL;
1438  bool found = false;
1439 
1440  mutt_buffer_reset(dest);
1441 
1442  for (p = fmt; *p; p++)
1443  {
1444  if (*p == '%')
1445  {
1446  switch (p[1])
1447  {
1448  case '%':
1449  mutt_buffer_addch(dest, *p++);
1450  break;
1451  case 's':
1452  found = true;
1453  mutt_buffer_addstr(dest, src);
1454  p++;
1455  break;
1456  default:
1457  mutt_buffer_addch(dest, *p);
1458  break;
1459  }
1460  }
1461  else
1462  {
1463  mutt_buffer_addch(dest, *p);
1464  }
1465  }
1466 
1467  if (!found)
1468  {
1469  mutt_buffer_addch(dest, ' ');
1470  mutt_buffer_addstr(dest, src);
1471  }
1472 }
1473 
1480 long mutt_file_get_size(const char *path)
1481 {
1482  if (!path)
1483  return 0;
1484 
1485  struct stat sb;
1486  if (stat(path, &sb) != 0)
1487  return 0;
1488 
1489  return sb.st_size;
1490 }
1491 
1501 {
1502  if (!a || !b)
1503  return 0;
1504  if (a->tv_sec < b->tv_sec)
1505  return -1;
1506  if (a->tv_sec > b->tv_sec)
1507  return 1;
1508 
1509  if (a->tv_nsec < b->tv_nsec)
1510  return -1;
1511  if (a->tv_nsec > b->tv_nsec)
1512  return 1;
1513  return 0;
1514 }
1515 
1522 void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
1523 {
1524  if (!dest || !sb)
1525  return;
1526 
1527  dest->tv_sec = 0;
1528  dest->tv_nsec = 0;
1529 
1530  switch (type)
1531  {
1532  case MUTT_STAT_ATIME:
1533  dest->tv_sec = sb->st_atime;
1534 #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
1535  dest->tv_nsec = sb->st_atim.tv_nsec;
1536 #endif
1537  break;
1538  case MUTT_STAT_MTIME:
1539  dest->tv_sec = sb->st_mtime;
1540 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1541  dest->tv_nsec = sb->st_mtim.tv_nsec;
1542 #endif
1543  break;
1544  case MUTT_STAT_CTIME:
1545  dest->tv_sec = sb->st_ctime;
1546 #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1547  dest->tv_nsec = sb->st_ctim.tv_nsec;
1548 #endif
1549  break;
1550  }
1551 }
1552 
1562 int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type,
1563  struct timespec *b)
1564 {
1565  if (!sba || !b)
1566  return 0;
1567 
1568  struct timespec a = { 0 };
1569 
1570  mutt_file_get_stat_timespec(&a, sba, type);
1571  return mutt_file_timespec_compare(&a, b);
1572 }
1573 
1584 int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type,
1585  struct stat *sbb, enum MuttStatType sbb_type)
1586 {
1587  if (!sba || !sbb)
1588  return 0;
1589 
1590  struct timespec a = { 0 };
1591  struct timespec b = { 0 };
1592 
1593  mutt_file_get_stat_timespec(&a, sba, sba_type);
1594  mutt_file_get_stat_timespec(&b, sbb, sbb_type);
1595  return mutt_file_timespec_compare(&a, &b);
1596 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:410
void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell&#39;s quoting rules.
Definition: file.c:832
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1500
#define MAX_LOCK_ATTEMPTS
Definition: file.c:61
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1015
#define NONULL(x)
Definition: string2.h:37
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
Memory management wrappers.
long mutt_file_get_size(const char *path)
Get the size of a file.
Definition: file.c:1480
File/dir&#39;s ctime - creation time.
Definition: file.h:61
char * line
the line data
Definition: file.h:69
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:992
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:166
#define mutt_message(...)
Definition: logging.h:83
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1332
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
Read the stat() time into a time value.
Definition: file.c:1522
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1131
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:192
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
int mutt_file_rmtree(const char *path)
Recursively remove a directory.
Definition: file.c:463
String manipulation buffer.
Definition: buffer.h:33
#define _(a)
Definition: message.h:28
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:662
static bool compare_stat(struct stat *osb, struct stat *nsb)
Compare the struct stat&#39;s of two files/dirs.
Definition: file.c:77
int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
Add permissions to a file.
Definition: file.c:1077
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
log_dispatcher_t MuttLogger
The log dispatcher.
Definition: logging.c:52
File management functions.
#define MUTT_CONT
-continuation
Definition: file.h:38
int mutt_file_chmod_rm(const char *path, mode_t mode)
Remove permissions from a file.
Definition: file.c:1109
Logging Dispatcher.
time_t tv_sec
Definition: file.h:47
String manipulation functions.
void mutt_file_unlink_empty(const char *path)
Delete a file if it&#39;s empty.
Definition: file.c:1296
static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
Create a temporary directory next to a file name.
Definition: file.c:91
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:871
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
const char * name
Definition: pgpmicalg.c:45
MuttStatType
Flags for mutt_file_get_stat_timespec.
Definition: file.h:57
#define SKIPWS(ch)
Definition: string2.h:47
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:293
int mutt_file_lock(int fd, bool excl, bool timeout)
File/dir&#39;s mtime - last modified time.
Definition: file.h:60
bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, int flags)
Process lines of text read from a file pointer.
Definition: file.c:762
int mutt_file_chmod(const char *path, mode_t mode)
Set permissions of a file.
Definition: file.c:1031
long tv_nsec
Definition: file.h:48
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, int flags)
iterate over the lines from an open file pointer
Definition: file.c:742
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
Message logging.
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:613
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:632
#define mutt_b2s(buf)
Definition: buffer.h:41
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:599
const char * line
Definition: common.c:36
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
int mutt_file_unlock(int fd)
#define PATH_MAX
Definition: mutt.h:52
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
int line_num
line number
Definition: file.h:71
void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1432
static const char rx_special_chars[]
Definition: file.c:56
char * data
Pointer to data.
Definition: buffer.h:35
size_t size
allocated size of line data
Definition: file.h:70
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
size_t mutt_file_quote_filename(const char *filename, char *buf, size_t buflen)
Quote a filename to survive the shell&#39;s quoting rules.
Definition: file.c:788
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:238
State record for mutt_file_iter_line()
Definition: file.h:67
Path manipulation functions.
int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1562
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
char * mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
Read a keyword from a file.
Definition: file.c:1367
int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type, struct stat *sbb, enum MuttStatType sbb_type)
Compare two stat infos.
Definition: file.c:1584
static int put_file_in_place(const char *path, const char *safe_file, const char *safe_dir)
Move a file into place.
Definition: file.c:134
bool(* mutt_file_map_t)(char *line, int line_num, void *user_data)
typedef mutt_file_map_t - Callback function for mutt_file_map_lines()
Definition: file.h:82
General purpose object for storing and parsing strings.
int mutt_file_open(const char *path, int flags)
Open a file.
Definition: file.c:517
Log at debug level 1.
Definition: logging.h:56
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:268
#define O_NOFOLLOW
Definition: file.c:65
#define mutt_error(...)
Definition: logging.h:84
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:53
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1397
void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1417
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file&#39;s modification time by 1 second.
Definition: file.c:959
#define FREE(x)
Definition: memory.h:40
Time value with nanosecond precision.
Definition: file.h:45
#define MUTT_EOL
don&#39;t strip \n / \r\n
Definition: file.h:39
const char filename_safe_chars[]
Definition: file.c:58
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1055
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:294
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:583
Time and date handling routines.
FILE * mutt_file_mkstemp_full(const char *file, int line, const char *func)
Create temporary file safely.
Definition: file.c:926
File/dir&#39;s atime - last accessed time.
Definition: file.h:59
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:349