NeoMutt  2019-12-07
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/stat.h>
41 #include <unistd.h>
42 #include <utime.h>
43 #include "file.h"
44 #include "buffer.h"
45 #include "date.h"
46 #include "logging.h"
47 #include "memory.h"
48 #include "message.h"
49 #include "path.h"
50 #include "string2.h"
51 #ifdef USE_FLOCK
52 #include <sys/file.h>
53 #endif
54 
55 char *C_Tmpdir;
56 
57 /* these characters must be escaped in regular expressions */
58 static const char rx_special_chars[] = "^.[$()|*+?{\\";
59 
60 const char filename_safe_chars[] =
61  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
62 
63 #define MAX_LOCK_ATTEMPTS 5
64 
65 /* This is defined in POSIX:2008 which isn't a build requirement */
66 #ifndef O_NOFOLLOW
67 #define O_NOFOLLOW 0
68 #endif
69 
79 static bool compare_stat(struct stat *osb, struct stat *nsb)
80 {
81  return (osb->st_dev == nsb->st_dev) && (osb->st_ino == nsb->st_ino) &&
82  (osb->st_rdev == nsb->st_rdev);
83 }
84 
93 static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
94 {
95  const char *basename = NULL;
96  int rc = 0;
97 
98  struct Buffer parent = mutt_buffer_make(PATH_MAX);
99  mutt_buffer_strcpy(&parent, NONULL(path));
100 
101  char *p = strrchr(parent.data, '/');
102  if (p)
103  {
104  *p = '\0';
105  basename = p + 1;
106  }
107  else
108  {
109  mutt_buffer_strcpy(&parent, ".");
110  basename = path;
111  }
112 
113  mutt_buffer_printf(newdir, "%s/%s", mutt_b2s(&parent), ".muttXXXXXX");
114  if (!mkdtemp(newdir->data))
115  {
116  mutt_debug(LL_DEBUG1, "mkdtemp() failed\n");
117  rc = -1;
118  goto cleanup;
119  }
120 
121  mutt_buffer_printf(newfile, "%s/%s", newdir->data, NONULL(basename));
122 
123 cleanup:
124  mutt_buffer_dealloc(&parent);
125  return rc;
126 }
127 
136 static int put_file_in_place(const char *path, const char *safe_file, const char *safe_dir)
137 {
138  int rc;
139 
140  rc = mutt_file_safe_rename(safe_file, path);
141  unlink(safe_file);
142  rmdir(safe_dir);
143  return rc;
144 }
145 
152 int mutt_file_fclose(FILE **fp)
153 {
154  if (!fp || !*fp)
155  return 0;
156 
157  int rc = fclose(*fp);
158  *fp = NULL;
159  return rc;
160 }
161 
168 int mutt_file_fsync_close(FILE **fp)
169 {
170  if (!fp || !*fp)
171  return 0;
172 
173  int rc = 0;
174 
175  if (fflush(*fp) || fsync(fileno(*fp)))
176  {
177  int save_errno = errno;
178  rc = -1;
179  mutt_file_fclose(fp);
180  errno = save_errno;
181  }
182  else
183  rc = mutt_file_fclose(fp);
184 
185  return rc;
186 }
187 
194 void mutt_file_unlink(const char *s)
195 {
196  if (!s)
197  return;
198 
199  struct stat sb;
200  /* Defend against symlink attacks */
201 
202  const bool is_regular_file = (lstat(s, &sb) == 0) && S_ISREG(sb.st_mode);
203  if (!is_regular_file)
204  return;
205 
206  const int fd = open(s, O_RDWR | O_NOFOLLOW);
207  if (fd < 0)
208  return;
209 
210  struct stat sb2;
211  if ((fstat(fd, &sb2) != 0) || !S_ISREG(sb2.st_mode) ||
212  (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
213  {
214  close(fd);
215  return;
216  }
217 
218  FILE *fp = fdopen(fd, "r+");
219  if (fp)
220  {
221  unlink(s);
222  char buf[2048] = { 0 };
223  while (sb.st_size > 0)
224  {
225  fwrite(buf, 1, MIN(sizeof(buf), sb.st_size), fp);
226  sb.st_size -= MIN(sizeof(buf), sb.st_size);
227  }
228  mutt_file_fclose(&fp);
229  }
230 }
231 
240 int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
241 {
242  if (!fp_in || !fp_out)
243  return -1;
244 
245  while (size > 0)
246  {
247  char buf[2048];
248  size_t chunk = (size > sizeof(buf)) ? sizeof(buf) : size;
249  chunk = fread(buf, 1, chunk, fp_in);
250  if (chunk < 1)
251  break;
252  if (fwrite(buf, 1, chunk, fp_out) != chunk)
253  return -1;
254 
255  size -= chunk;
256  }
257 
258  if (fflush(fp_out) != 0)
259  return -1;
260  return 0;
261 }
262 
270 int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
271 {
272  if (!fp_in || !fp_out)
273  return -1;
274 
275  size_t l;
276  char buf[1024];
277 
278  while ((l = fread(buf, 1, sizeof(buf), fp_in)) > 0)
279  {
280  if (fwrite(buf, 1, l, fp_out) != l)
281  return -1;
282  }
283 
284  if (fflush(fp_out) != 0)
285  return -1;
286  return 0;
287 }
288 
296 int mutt_file_symlink(const char *oldpath, const char *newpath)
297 {
298  struct stat osb, nsb;
299 
300  if (!oldpath || !newpath)
301  return -1;
302 
303  if ((unlink(newpath) == -1) && (errno != ENOENT))
304  return -1;
305 
306  if (oldpath[0] == '/')
307  {
308  if (symlink(oldpath, newpath) == -1)
309  return -1;
310  }
311  else
312  {
313  struct Buffer abs_oldpath = mutt_buffer_make(PATH_MAX);
314 
315  if (!mutt_path_getcwd(&abs_oldpath))
316  {
317  mutt_buffer_dealloc(&abs_oldpath);
318  return -1;
319  }
320 
321  mutt_buffer_addch(&abs_oldpath, '/');
322  mutt_buffer_addstr(&abs_oldpath, oldpath);
323  if (symlink(mutt_b2s(&abs_oldpath), newpath) == -1)
324  {
325  mutt_buffer_dealloc(&abs_oldpath);
326  return -1;
327  }
328 
329  mutt_buffer_dealloc(&abs_oldpath);
330  }
331 
332  if ((stat(oldpath, &osb) == -1) || (stat(newpath, &nsb) == -1) ||
333  !compare_stat(&osb, &nsb))
334  {
335  unlink(newpath);
336  return -1;
337  }
338 
339  return 0;
340 }
341 
351 int mutt_file_safe_rename(const char *src, const char *target)
352 {
353  struct stat ssb, tsb;
354  int link_errno;
355 
356  if (!src || !target)
357  return -1;
358 
359  if (link(src, target) != 0)
360  {
361  link_errno = errno;
362 
363  /* It is historically documented that link can return -1 if NFS
364  * dies after creating the link. In that case, we are supposed
365  * to use stat to check if the link was created.
366  *
367  * Derek Martin notes that some implementations of link() follow a
368  * source symlink. It might be more correct to use stat() on src.
369  * I am not doing so to minimize changes in behavior: the function
370  * used lstat() further below for 20 years without issue, and I
371  * believe was never intended to be used on a src symlink. */
372  if ((lstat(src, &ssb) == 0) && (lstat(target, &tsb) == 0) &&
373  (compare_stat(&ssb, &tsb) == 0))
374  {
375  mutt_debug(LL_DEBUG1, "link (%s, %s) reported failure: %s (%d) but actually succeeded\n",
376  src, target, strerror(errno), errno);
377  goto success;
378  }
379 
380  errno = link_errno;
381 
382  /* Coda does not allow cross-directory links, but tells
383  * us it's a cross-filesystem linking attempt.
384  *
385  * However, the Coda rename call is allegedly safe to use.
386  *
387  * With other file systems, rename should just fail when
388  * the files reside on different file systems, so it's safe
389  * to try it here. */
390  mutt_debug(LL_DEBUG1, "link (%s, %s) failed: %s (%d)\n", src, target,
391  strerror(errno), errno);
392 
393  /* FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
394  * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear. */
395  if ((errno == EXDEV) || (errno == ENOSYS) || errno == EPERM
396 #ifdef ENOTSUP
397  || errno == ENOTSUP
398 #endif
399 #ifdef EOPNOTSUPP
400  || errno == EOPNOTSUPP
401 #endif
402  )
403  {
404  mutt_debug(LL_DEBUG1, "trying rename\n");
405  if (rename(src, target) == -1)
406  {
407  mutt_debug(LL_DEBUG1, "rename (%s, %s) failed: %s (%d)\n", src, target,
408  strerror(errno), errno);
409  return -1;
410  }
411  mutt_debug(LL_DEBUG1, "rename succeeded\n");
412 
413  return 0;
414  }
415 
416  return -1;
417  }
418 
419  /* Remove the compare_stat() check, because it causes problems with maildir
420  * on filesystems that don't properly support hard links, such as sshfs. The
421  * filesystem creates the link, but the resulting file is given a different
422  * inode number by the sshfs layer. This results in an infinite loop
423  * creating links. */
424 #if 0
425  /* Stat both links and check if they are equal. */
426  if (lstat(src, &ssb) == -1)
427  {
428  mutt_debug(LL_DEBUG1, "#1 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
429  return -1;
430  }
431 
432  if (lstat(target, &tsb) == -1)
433  {
434  mutt_debug(LL_DEBUG1, "#2 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
435  return -1;
436  }
437 
438  /* pretend that the link failed because the target file did already exist. */
439 
440  if (!compare_stat(&ssb, &tsb))
441  {
442  mutt_debug(LL_DEBUG1, "stat blocks for %s and %s diverge; pretending EEXIST\n", src, target);
443  errno = EEXIST;
444  return -1;
445  }
446 #endif
447 
448 success:
449  /* Unlink the original link.
450  * Should we really ignore the return value here? XXX */
451  if (unlink(src) == -1)
452  {
453  mutt_debug(LL_DEBUG1, "unlink (%s) failed: %s (%d)\n", src, strerror(errno), errno);
454  }
455 
456  return 0;
457 }
458 
465 int mutt_file_rmtree(const char *path)
466 {
467  if (!path)
468  return -1;
469 
470  struct dirent *de = NULL;
471  struct stat statbuf;
472  int rc = 0;
473 
474  DIR *dirp = opendir(path);
475  if (!dirp)
476  {
477  mutt_debug(LL_DEBUG1, "error opening directory %s\n", path);
478  return -1;
479  }
480 
481  /* We avoid using the buffer pool for this function, because it
482  * invokes recursively to an unknown depth. */
483  struct Buffer cur = mutt_buffer_make(PATH_MAX);
484 
485  while ((de = readdir(dirp)))
486  {
487  if ((strcmp(".", de->d_name) == 0) || (strcmp("..", de->d_name) == 0))
488  continue;
489 
490  mutt_buffer_printf(&cur, "%s/%s", path, de->d_name);
491  /* XXX make nonrecursive version */
492 
493  if (stat(mutt_b2s(&cur), &statbuf) == -1)
494  {
495  rc = 1;
496  continue;
497  }
498 
499  if (S_ISDIR(statbuf.st_mode))
500  rc |= mutt_file_rmtree(mutt_b2s(&cur));
501  else
502  rc |= unlink(mutt_b2s(&cur));
503  }
504  closedir(dirp);
505 
506  rc |= rmdir(path);
507 
508  mutt_buffer_dealloc(&cur);
509  return rc;
510 }
511 
519 int mutt_file_open(const char *path, int flags)
520 {
521  if (!path)
522  return -1;
523 
524  struct stat osb, nsb;
525  int fd;
526  struct Buffer safe_file = mutt_buffer_make(0);
527  struct Buffer safe_dir = mutt_buffer_make(0);
528 
529  if (flags & O_EXCL)
530  {
531  mutt_buffer_alloc(&safe_file, PATH_MAX);
532  mutt_buffer_alloc(&safe_dir, PATH_MAX);
533 
534  if (mkwrapdir(path, &safe_file, &safe_dir) == -1)
535  {
536  fd = -1;
537  goto cleanup;
538  }
539 
540  fd = open(mutt_b2s(&safe_file), flags, 0600);
541  if (fd < 0)
542  {
543  rmdir(mutt_b2s(&safe_dir));
544  goto cleanup;
545  }
546 
547  /* NFS and I believe cygwin do not handle movement of open files well */
548  close(fd);
549  if (put_file_in_place(path, mutt_b2s(&safe_file), mutt_b2s(&safe_dir)) == -1)
550  {
551  fd = -1;
552  goto cleanup;
553  }
554  }
555 
556  fd = open(path, flags & ~O_EXCL, 0600);
557  if (fd < 0)
558  goto cleanup;
559 
560  /* make sure the file is not symlink */
561  if (((lstat(path, &osb) < 0) || (fstat(fd, &nsb) < 0)) || !compare_stat(&osb, &nsb))
562  {
563  close(fd);
564  fd = -1;
565  goto cleanup;
566  }
567 
568 cleanup:
569  mutt_buffer_dealloc(&safe_file);
570  mutt_buffer_dealloc(&safe_dir);
571 
572  return fd;
573 }
574 
585 FILE *mutt_file_fopen(const char *path, const char *mode)
586 {
587  if (!path || !mode)
588  return NULL;
589 
590  if (mode[0] == 'w')
591  {
592  int fd;
593  int flags = O_CREAT | O_EXCL | O_NOFOLLOW;
594 
595  if (mode[1] == '+')
596  flags |= O_RDWR;
597  else
598  flags |= O_WRONLY;
599 
600  fd = mutt_file_open(path, flags);
601  if (fd < 0)
602  return NULL;
603 
604  return fdopen(fd, mode);
605  }
606  else
607  return fopen(path, mode);
608 }
609 
615 void mutt_file_sanitize_filename(char *path, bool slash)
616 {
617  if (!path)
618  return;
619 
620  for (; *path; path++)
621  {
622  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
623  *path = '_';
624  }
625 }
626 
634 int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
635 {
636  if (!dest || !src)
637  return -1;
638 
639  mutt_buffer_reset(dest);
640  while (*src != '\0')
641  {
642  if (strchr(rx_special_chars, *src))
643  mutt_buffer_addch(dest, '\\');
644  mutt_buffer_addch(dest, *src++);
645  }
646 
647  return 0;
648 }
649 
664 char *mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
665 {
666  if (!size || !fp)
667  return NULL;
668 
669  size_t offset = 0;
670  char *ch = NULL;
671 
672  if (!line)
673  {
674  *size = 256;
675  line = mutt_mem_malloc(*size);
676  }
677 
678  while (true)
679  {
680  if (!fgets(line + offset, *size - offset, fp))
681  {
682  FREE(&line);
683  return NULL;
684  }
685  ch = strchr(line + offset, '\n');
686  if (ch)
687  {
688  if (line_num)
689  (*line_num)++;
690  if (flags & MUTT_EOL)
691  return line;
692  *ch = '\0';
693  if ((ch > line) && (*(ch - 1) == '\r'))
694  *--ch = '\0';
695  if (!(flags & MUTT_CONT) || (ch == line) || (*(ch - 1) != '\\'))
696  return line;
697  offset = ch - line - 1;
698  }
699  else
700  {
701  int c;
702  c = getc(fp); /* This is kind of a hack. We want to know if the
703  char at the current point in the input stream is EOF.
704  feof() will only tell us if we've already hit EOF, not
705  if the next character is EOF. So, we need to read in
706  the next character and manually check if it is EOF. */
707  if (c == EOF)
708  {
709  /* The last line of fp isn't \n terminated */
710  if (line_num)
711  (*line_num)++;
712  return line;
713  }
714  else
715  {
716  ungetc(c, fp); /* undo our damage */
717  /* There wasn't room for the line -- increase "line" */
718  offset = *size - 1; /* overwrite the terminating 0 */
719  *size += 256;
720  mutt_mem_realloc(&line, *size);
721  }
722  }
723  }
724 }
725 
744 bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, int flags)
745 {
746  if (!iter)
747  return false;
748 
749  char *p = mutt_file_read_line(iter->line, &iter->size, fp, &iter->line_num, flags);
750  if (!p)
751  return false;
752  iter->line = p;
753  return true;
754 }
755 
764 bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, int flags)
765 {
766  if (!func || !fp)
767  return false;
768 
769  struct MuttFileIter iter = { 0 };
770  while (mutt_file_iter_line(&iter, fp, flags))
771  {
772  if (!(*func)(iter.line, iter.line_num, user_data))
773  {
774  FREE(&iter.line);
775  return false;
776  }
777  }
778  return true;
779 }
780 
790 size_t mutt_file_quote_filename(const char *filename, char *buf, size_t buflen)
791 {
792  if (!buf)
793  return 0;
794 
795  if (!filename)
796  {
797  *buf = '\0';
798  return 0;
799  }
800 
801  size_t j = 0;
802 
803  /* leave some space for the trailing characters. */
804  buflen -= 6;
805 
806  buf[j++] = '\'';
807 
808  for (size_t i = 0; (j < buflen) && filename[i]; i++)
809  {
810  if ((filename[i] == '\'') || (filename[i] == '`'))
811  {
812  buf[j++] = '\'';
813  buf[j++] = '\\';
814  buf[j++] = filename[i];
815  buf[j++] = '\'';
816  }
817  else
818  buf[j++] = filename[i];
819  }
820 
821  buf[j++] = '\'';
822  buf[j] = '\0';
823 
824  return j;
825 }
826 
834 void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
835 {
836  if (!buf || !filename)
837  return;
838 
839  mutt_buffer_reset(buf);
840  if (add_outer)
841  mutt_buffer_addch(buf, '\'');
842 
843  for (; *filename != '\0'; filename++)
844  {
845  if ((*filename == '\'') || (*filename == '`'))
846  {
847  mutt_buffer_addch(buf, '\'');
848  mutt_buffer_addch(buf, '\\');
849  mutt_buffer_addch(buf, *filename);
850  mutt_buffer_addch(buf, '\'');
851  }
852  else
853  mutt_buffer_addch(buf, *filename);
854  }
855 
856  if (add_outer)
857  mutt_buffer_addch(buf, '\'');
858 }
859 
873 int mutt_file_mkdir(const char *path, mode_t mode)
874 {
875  if (!path || !*path)
876  {
877  errno = EINVAL;
878  return -1;
879  }
880 
881  errno = 0;
882  char tmp_path[PATH_MAX];
883  const size_t len = strlen(path);
884 
885  if (len >= sizeof(tmp_path))
886  {
887  errno = ENAMETOOLONG;
888  return -1;
889  }
890 
891  struct stat sb;
892  if ((stat(path, &sb) == 0) && S_ISDIR(sb.st_mode))
893  return 0;
894 
895  /* Create a mutable copy */
896  mutt_str_strfcpy(tmp_path, path, sizeof(tmp_path));
897 
898  for (char *p = tmp_path + 1; *p; p++)
899  {
900  if (*p != '/')
901  continue;
902 
903  /* Temporarily truncate the path */
904  *p = '\0';
905 
906  if ((mkdir(tmp_path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) && (errno != EEXIST))
907  return -1;
908 
909  *p = '/';
910  }
911 
912  if ((mkdir(tmp_path, mode) != 0) && (errno != EEXIST))
913  return -1;
914 
915  return 0;
916 }
917 
928 FILE *mutt_file_mkstemp_full(const char *file, int line, const char *func)
929 {
930  char name[PATH_MAX];
931 
932  int n = snprintf(name, sizeof(name), "%s/neomutt-XXXXXX", NONULL(C_Tmpdir));
933  if (n < 0)
934  return NULL;
935 
936  int fd = mkstemp(name);
937  if (fd == -1)
938  return NULL;
939 
940  FILE *fp = fdopen(fd, "w+");
941 
942  if ((unlink(name) != 0) && (errno != ENOENT))
943  {
944  mutt_file_fclose(&fp);
945  return NULL;
946  }
947 
948  MuttLogger(0, file, line, func, 1, "created temp file '%s'\n", name);
949  return fp;
950 }
951 
961 time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
962 {
963  if (!fp)
964  return -1;
965 
966  struct utimbuf utim;
967  struct stat st2;
968  time_t mtime;
969 
970  if (!st)
971  {
972  if (stat(fp, &st2) == -1)
973  return -1;
974  st = &st2;
975  }
976 
977  mtime = st->st_mtime;
978  if (mtime == mutt_date_epoch())
979  {
980  mtime -= 1;
981  utim.actime = mtime;
982  utim.modtime = mtime;
983  utime(fp, &utim);
984  }
985 
986  return mtime;
987 }
988 
994 void mutt_file_set_mtime(const char *from, const char *to)
995 {
996  if (!from || !to)
997  return;
998 
999  struct utimbuf utim;
1000  struct stat st;
1001 
1002  if (stat(from, &st) != -1)
1003  {
1004  utim.actime = st.st_mtime;
1005  utim.modtime = st.st_mtime;
1006  utime(to, &utim);
1007  }
1008 }
1009 
1018 {
1019 #ifdef HAVE_FUTIMENS
1020  struct timespec times[2] = { { 0, UTIME_NOW }, { 0, UTIME_OMIT } };
1021  futimens(fd, times);
1022 #endif
1023 }
1024 
1033 int mutt_file_chmod(const char *path, mode_t mode)
1034 {
1035  if (!path)
1036  return -1;
1037 
1038  return chmod(path, mode);
1039 }
1040 
1058 int mutt_file_chmod_add(const char *path, mode_t mode)
1059 {
1060  return mutt_file_chmod_add_stat(path, mode, NULL);
1061 }
1062 
1081 int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
1082 {
1083  if (!path)
1084  return -1;
1085 
1086  struct stat st2;
1087 
1088  if (!st)
1089  {
1090  if (stat(path, &st2) == -1)
1091  return -1;
1092  st = &st2;
1093  }
1094  return chmod(path, st->st_mode | mode);
1095 }
1096 
1114 int mutt_file_chmod_rm(const char *path, mode_t mode)
1115 {
1116  return mutt_file_chmod_rm_stat(path, mode, NULL);
1117 }
1118 
1137 int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
1138 {
1139  if (!path)
1140  return -1;
1141 
1142  struct stat st2;
1143 
1144  if (!st)
1145  {
1146  if (stat(path, &st2) == -1)
1147  return -1;
1148  st = &st2;
1149  }
1150  return chmod(path, st->st_mode & ~mode);
1151 }
1152 
1153 #if defined(USE_FCNTL)
1154 
1166 int mutt_file_lock(int fd, bool excl, bool timeout)
1167 {
1168  struct stat sb = { 0 }, prev_sb = { 0 };
1169  int count = 0;
1170  int attempt = 0;
1171 
1172  struct flock lck;
1173  memset(&lck, 0, sizeof(struct flock));
1174  lck.l_type = excl ? F_WRLCK : F_RDLCK;
1175  lck.l_whence = SEEK_SET;
1176 
1177  while (fcntl(fd, F_SETLK, &lck) == -1)
1178  {
1179  mutt_debug(LL_DEBUG1, "fcntl errno %d\n", errno);
1180  if ((errno != EAGAIN) && (errno != EACCES))
1181  {
1182  mutt_perror("fcntl");
1183  return -1;
1184  }
1185 
1186  if (fstat(fd, &sb) != 0)
1187  sb.st_size = 0;
1188 
1189  if (count == 0)
1190  prev_sb = sb;
1191 
1192  /* only unlock file if it is unchanged */
1193  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1194  {
1195  if (timeout)
1196  mutt_error(_("Timeout exceeded while attempting fcntl lock"));
1197  return -1;
1198  }
1199 
1200  prev_sb = sb;
1201 
1202  mutt_message(_("Waiting for fcntl lock... %d"), ++attempt);
1203  sleep(1);
1204  }
1205 
1206  return 0;
1207 }
1208 
1214 int mutt_file_unlock(int fd)
1215 {
1216  struct flock unlockit;
1217 
1218  memset(&unlockit, 0, sizeof(struct flock));
1219  unlockit.l_type = F_UNLCK;
1220  unlockit.l_whence = SEEK_SET;
1221  fcntl(fd, F_SETLK, &unlockit);
1222 
1223  return 0;
1224 }
1225 #elif defined(USE_FLOCK)
1226 
1238 int mutt_file_lock(int fd, bool excl, bool timeout)
1239 {
1240  struct stat sb = { 0 }, prev_sb = { 0 };
1241  int rc = 0;
1242  int count = 0;
1243  int attempt = 0;
1244 
1245  while (flock(fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
1246  {
1247  if (errno != EWOULDBLOCK)
1248  {
1249  mutt_perror("flock");
1250  rc = -1;
1251  break;
1252  }
1253 
1254  if (fstat(fd, &sb) != 0)
1255  sb.st_size = 0;
1256 
1257  if (count == 0)
1258  prev_sb = sb;
1259 
1260  /* only unlock file if it is unchanged */
1261  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1262  {
1263  if (timeout)
1264  mutt_error(_("Timeout exceeded while attempting flock lock"));
1265  rc = -1;
1266  break;
1267  }
1268 
1269  prev_sb = sb;
1270 
1271  mutt_message(_("Waiting for flock attempt... %d"), ++attempt);
1272  sleep(1);
1273  }
1274 
1275  /* release any other locks obtained in this routine */
1276  if (rc != 0)
1277  {
1278  flock(fd, LOCK_UN);
1279  }
1280 
1281  return rc;
1282 }
1283 
1289 int mutt_file_unlock(int fd)
1290 {
1291  flock(fd, LOCK_UN);
1292  return 0;
1293 }
1294 #else
1295 #error "You must select a locking mechanism via USE_FCNTL or USE_FLOCK"
1296 #endif
1297 
1302 void mutt_file_unlink_empty(const char *path)
1303 {
1304  if (!path)
1305  return;
1306 
1307  struct stat sb;
1308 
1309  int fd = open(path, O_RDWR);
1310  if (fd == -1)
1311  return;
1312 
1313  if (mutt_file_lock(fd, true, true) == -1)
1314  {
1315  close(fd);
1316  return;
1317  }
1318 
1319  if ((fstat(fd, &sb) == 0) && (sb.st_size == 0))
1320  unlink(path);
1321 
1322  mutt_file_unlock(fd);
1323  close(fd);
1324 }
1325 
1338 int mutt_file_rename(const char *oldfile, const char *newfile)
1339 {
1340  if (!oldfile || !newfile)
1341  return -1;
1342  if (access(oldfile, F_OK) != 0)
1343  return 1;
1344  if (access(newfile, F_OK) == 0)
1345  return 2;
1346 
1347  FILE *fp_old = fopen(oldfile, "r");
1348  if (!fp_old)
1349  return 3;
1350  FILE *fp_new = mutt_file_fopen(newfile, "w");
1351  if (!fp_new)
1352  {
1353  mutt_file_fclose(&fp_old);
1354  return 3;
1355  }
1356  mutt_file_copy_stream(fp_old, fp_new);
1357  mutt_file_fclose(&fp_new);
1358  mutt_file_fclose(&fp_old);
1359  mutt_file_unlink(oldfile);
1360  return 0;
1361 }
1362 
1373 char *mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
1374 {
1375  FILE *fp = mutt_file_fopen(file, "r");
1376  if (!fp)
1377  return NULL;
1378 
1379  buf = fgets(buf, buflen, fp);
1380  mutt_file_fclose(&fp);
1381 
1382  if (!buf)
1383  return NULL;
1384 
1385  SKIPWS(buf);
1386  char *start = buf;
1387 
1388  while ((*buf != '\0') && !isspace(*buf))
1389  buf++;
1390 
1391  *buf = '\0';
1392 
1393  return start;
1394 }
1395 
1403 int mutt_file_check_empty(const char *path)
1404 {
1405  if (!path)
1406  return -1;
1407 
1408  struct stat st;
1409  if (stat(path, &st) == -1)
1410  return -1;
1411 
1412  return st.st_size == 0;
1413 }
1414 
1423 void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
1424 {
1425  struct Buffer tmp = mutt_buffer_make(PATH_MAX);
1426 
1427  mutt_buffer_quote_filename(&tmp, src, true);
1428  mutt_file_expand_fmt(dest, fmt, mutt_b2s(&tmp));
1429  mutt_buffer_dealloc(&tmp);
1430 }
1431 
1438 void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
1439 {
1440  if (!dest || !fmt || !src)
1441  return;
1442 
1443  const char *p = NULL;
1444  bool found = false;
1445 
1446  mutt_buffer_reset(dest);
1447 
1448  for (p = fmt; *p; p++)
1449  {
1450  if (*p == '%')
1451  {
1452  switch (p[1])
1453  {
1454  case '%':
1455  mutt_buffer_addch(dest, *p++);
1456  break;
1457  case 's':
1458  found = true;
1459  mutt_buffer_addstr(dest, src);
1460  p++;
1461  break;
1462  default:
1463  mutt_buffer_addch(dest, *p);
1464  break;
1465  }
1466  }
1467  else
1468  {
1469  mutt_buffer_addch(dest, *p);
1470  }
1471  }
1472 
1473  if (!found)
1474  {
1475  mutt_buffer_addch(dest, ' ');
1476  mutt_buffer_addstr(dest, src);
1477  }
1478 }
1479 
1486 long mutt_file_get_size(const char *path)
1487 {
1488  if (!path)
1489  return 0;
1490 
1491  struct stat sb;
1492  if (stat(path, &sb) != 0)
1493  return 0;
1494 
1495  return sb.st_size;
1496 }
1497 
1507 {
1508  if (!a || !b)
1509  return 0;
1510  if (a->tv_sec < b->tv_sec)
1511  return -1;
1512  if (a->tv_sec > b->tv_sec)
1513  return 1;
1514 
1515  if (a->tv_nsec < b->tv_nsec)
1516  return -1;
1517  if (a->tv_nsec > b->tv_nsec)
1518  return 1;
1519  return 0;
1520 }
1521 
1528 void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
1529 {
1530  if (!dest || !sb)
1531  return;
1532 
1533  dest->tv_sec = 0;
1534  dest->tv_nsec = 0;
1535 
1536  switch (type)
1537  {
1538  case MUTT_STAT_ATIME:
1539  dest->tv_sec = sb->st_atime;
1540 #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
1541  dest->tv_nsec = sb->st_atim.tv_nsec;
1542 #endif
1543  break;
1544  case MUTT_STAT_MTIME:
1545  dest->tv_sec = sb->st_mtime;
1546 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1547  dest->tv_nsec = sb->st_mtim.tv_nsec;
1548 #endif
1549  break;
1550  case MUTT_STAT_CTIME:
1551  dest->tv_sec = sb->st_ctime;
1552 #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1553  dest->tv_nsec = sb->st_ctim.tv_nsec;
1554 #endif
1555  break;
1556  }
1557 }
1558 
1568 int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type,
1569  struct timespec *b)
1570 {
1571  if (!sba || !b)
1572  return 0;
1573 
1574  struct timespec a = { 0 };
1575 
1576  mutt_file_get_stat_timespec(&a, sba, type);
1577  return mutt_file_timespec_compare(&a, b);
1578 }
1579 
1590 int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type,
1591  struct stat *sbb, enum MuttStatType sbb_type)
1592 {
1593  if (!sba || !sbb)
1594  return 0;
1595 
1596  struct timespec a = { 0 };
1597  struct timespec b = { 0 };
1598 
1599  mutt_file_get_stat_timespec(&a, sba, sba_type);
1600  mutt_file_get_stat_timespec(&b, sbb, sbb_type);
1601  return mutt_file_timespec_compare(&a, &b);
1602 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:411
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:834
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1506
#define MAX_LOCK_ATTEMPTS
Definition: file.c:63
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1017
#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:1486
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:994
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:168
#define mutt_message(...)
Definition: logging.h:83
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1338
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:1528
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1137
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
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:465
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:664
static bool compare_stat(struct stat *osb, struct stat *nsb)
Compare the struct stat&#39;s of two files/dirs.
Definition: file.c:79
int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
Add permissions to a file.
Definition: file.c:1081
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:1114
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:1302
static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
Create a temporary directory next to a file name.
Definition: file.c:93
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:873
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
const char * name
Definition: pgpmicalg.c:46
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:294
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:764
int mutt_file_chmod(const char *path, mode_t mode)
Set permissions of a file.
Definition: file.c:1033
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:744
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:615
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:634
#define mutt_b2s(buf)
Definition: buffer.h:41
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:541
const char * line
Definition: common.c:36
int mutt_file_unlock(int fd)
#define PATH_MAX
Definition: mutt.h:50
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:1438
static const char rx_special_chars[]
Definition: file.c:58
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:790
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:240
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:1568
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:1373
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:1590
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
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:136
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:519
Log at debug level 1.
Definition: logging.h:40
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:270
#define O_NOFOLLOW
Definition: file.c:67
#define mutt_error(...)
Definition: logging.h:84
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:55
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1403
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:1423
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:961
#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:60
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1058
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:296
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:585
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:928
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:351