NeoMutt  2021-02-05-666-ge300cd
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 <time.h>
42 #include <unistd.h>
43 #include <utime.h>
44 #include "config/lib.h"
45 #include "core/lib.h"
46 #include "file.h"
47 #include "buffer.h"
48 #include "date.h"
49 #include "logging.h"
50 #include "memory.h"
51 #include "message.h"
52 #include "path.h"
53 #include "string2.h"
54 #ifdef USE_FLOCK
55 #include <sys/file.h>
56 #endif
57 
58 /* these characters must be escaped in regular expressions */
59 static const char rx_special_chars[] = "^.[$()|*+?{\\";
60 
61 const char filename_safe_chars[] =
62  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
63 
64 #define MAX_LOCK_ATTEMPTS 5
65 
66 /* This is defined in POSIX:2008 which isn't a build requirement */
67 #ifndef O_NOFOLLOW
68 #define O_NOFOLLOW 0
69 #endif
70 
80 static bool compare_stat(struct stat *osb, struct stat *nsb)
81 {
82  return (osb->st_dev == nsb->st_dev) && (osb->st_ino == nsb->st_ino) &&
83  (osb->st_rdev == nsb->st_rdev);
84 }
85 
94 static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
95 {
96  const char *basename = NULL;
97  int rc = 0;
98 
99  struct Buffer parent = mutt_buffer_make(PATH_MAX);
100  mutt_buffer_strcpy(&parent, NONULL(path));
101 
102  char *p = strrchr(parent.data, '/');
103  if (p)
104  {
105  *p = '\0';
106  basename = p + 1;
107  }
108  else
109  {
110  mutt_buffer_strcpy(&parent, ".");
111  basename = path;
112  }
113 
114  mutt_buffer_printf(newdir, "%s/%s", mutt_buffer_string(&parent), ".muttXXXXXX");
115  if (!mkdtemp(newdir->data))
116  {
117  mutt_debug(LL_DEBUG1, "mkdtemp() failed\n");
118  rc = -1;
119  goto cleanup;
120  }
121 
122  mutt_buffer_printf(newfile, "%s/%s", newdir->data, NONULL(basename));
123 
124 cleanup:
125  mutt_buffer_dealloc(&parent);
126  return rc;
127 }
128 
137 static int put_file_in_place(const char *path, const char *safe_file, const char *safe_dir)
138 {
139  int rc;
140 
141  rc = mutt_file_safe_rename(safe_file, path);
142  unlink(safe_file);
143  rmdir(safe_dir);
144  return rc;
145 }
146 
153 int mutt_file_fclose(FILE **fp)
154 {
155  if (!fp || !*fp)
156  return 0;
157 
158  int rc = fclose(*fp);
159  *fp = NULL;
160  return rc;
161 }
162 
169 int mutt_file_fsync_close(FILE **fp)
170 {
171  if (!fp || !*fp)
172  return 0;
173 
174  int rc = 0;
175 
176  if (fflush(*fp) || fsync(fileno(*fp)))
177  {
178  int save_errno = errno;
179  rc = -1;
180  mutt_file_fclose(fp);
181  errno = save_errno;
182  }
183  else
184  rc = mutt_file_fclose(fp);
185 
186  return rc;
187 }
188 
195 void mutt_file_unlink(const char *s)
196 {
197  if (!s)
198  return;
199 
200  struct stat sb;
201  /* Defend against symlink attacks */
202 
203  const bool is_regular_file = (lstat(s, &sb) == 0) && S_ISREG(sb.st_mode);
204  if (!is_regular_file)
205  return;
206 
207  const int fd = open(s, O_RDWR | O_NOFOLLOW);
208  if (fd < 0)
209  return;
210 
211  struct stat sb2;
212  if ((fstat(fd, &sb2) != 0) || !S_ISREG(sb2.st_mode) ||
213  (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
214  {
215  close(fd);
216  return;
217  }
218 
219  FILE *fp = fdopen(fd, "r+");
220  if (fp)
221  {
222  unlink(s);
223  char buf[2048] = { 0 };
224  while (sb.st_size > 0)
225  {
226  fwrite(buf, 1, MIN(sizeof(buf), sb.st_size), fp);
227  sb.st_size -= MIN(sizeof(buf), sb.st_size);
228  }
229  mutt_file_fclose(&fp);
230  }
231 }
232 
241 int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
242 {
243  if (!fp_in || !fp_out)
244  return -1;
245 
246  while (size > 0)
247  {
248  char buf[2048];
249  size_t chunk = (size > sizeof(buf)) ? sizeof(buf) : size;
250  chunk = fread(buf, 1, chunk, fp_in);
251  if (chunk < 1)
252  break;
253  if (fwrite(buf, 1, chunk, fp_out) != chunk)
254  return -1;
255 
256  size -= chunk;
257  }
258 
259  if (fflush(fp_out) != 0)
260  return -1;
261  return 0;
262 }
263 
271 int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
272 {
273  if (!fp_in || !fp_out)
274  return -1;
275 
276  size_t total = 0;
277  size_t l;
278  char buf[1024];
279 
280  while ((l = fread(buf, 1, sizeof(buf), fp_in)) > 0)
281  {
282  if (fwrite(buf, 1, l, fp_out) != l)
283  return -1;
284  total += l;
285  }
286 
287  if (fflush(fp_out) != 0)
288  return -1;
289  return total;
290 }
291 
299 int mutt_file_symlink(const char *oldpath, const char *newpath)
300 {
301  struct stat osb, nsb;
302 
303  if (!oldpath || !newpath)
304  return -1;
305 
306  if ((unlink(newpath) == -1) && (errno != ENOENT))
307  return -1;
308 
309  if (oldpath[0] == '/')
310  {
311  if (symlink(oldpath, newpath) == -1)
312  return -1;
313  }
314  else
315  {
316  struct Buffer abs_oldpath = mutt_buffer_make(PATH_MAX);
317 
318  if (!mutt_path_getcwd(&abs_oldpath))
319  {
320  mutt_buffer_dealloc(&abs_oldpath);
321  return -1;
322  }
323 
324  mutt_buffer_addch(&abs_oldpath, '/');
325  mutt_buffer_addstr(&abs_oldpath, oldpath);
326  if (symlink(mutt_buffer_string(&abs_oldpath), newpath) == -1)
327  {
328  mutt_buffer_dealloc(&abs_oldpath);
329  return -1;
330  }
331 
332  mutt_buffer_dealloc(&abs_oldpath);
333  }
334 
335  if ((stat(oldpath, &osb) == -1) || (stat(newpath, &nsb) == -1) ||
336  !compare_stat(&osb, &nsb))
337  {
338  unlink(newpath);
339  return -1;
340  }
341 
342  return 0;
343 }
344 
354 int mutt_file_safe_rename(const char *src, const char *target)
355 {
356  struct stat ssb, tsb;
357  int link_errno;
358 
359  if (!src || !target)
360  return -1;
361 
362  if (link(src, target) != 0)
363  {
364  link_errno = errno;
365 
366  /* It is historically documented that link can return -1 if NFS
367  * dies after creating the link. In that case, we are supposed
368  * to use stat to check if the link was created.
369  *
370  * Derek Martin notes that some implementations of link() follow a
371  * source symlink. It might be more correct to use stat() on src.
372  * I am not doing so to minimize changes in behavior: the function
373  * used lstat() further below for 20 years without issue, and I
374  * believe was never intended to be used on a src symlink. */
375  if ((lstat(src, &ssb) == 0) && (lstat(target, &tsb) == 0) &&
376  (compare_stat(&ssb, &tsb) == 0))
377  {
378  mutt_debug(LL_DEBUG1, "link (%s, %s) reported failure: %s (%d) but actually succeeded\n",
379  src, target, strerror(errno), errno);
380  goto success;
381  }
382 
383  errno = link_errno;
384 
385  /* Coda does not allow cross-directory links, but tells
386  * us it's a cross-filesystem linking attempt.
387  *
388  * However, the Coda rename call is allegedly safe to use.
389  *
390  * With other file systems, rename should just fail when
391  * the files reside on different file systems, so it's safe
392  * to try it here. */
393  mutt_debug(LL_DEBUG1, "link (%s, %s) failed: %s (%d)\n", src, target,
394  strerror(errno), errno);
395 
396  /* FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
397  * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear. */
398  if ((errno == EXDEV) || (errno == ENOSYS) || errno == EPERM
399 #ifdef ENOTSUP
400  || errno == ENOTSUP
401 #endif
402 #ifdef EOPNOTSUPP
403  || errno == EOPNOTSUPP
404 #endif
405  )
406  {
407  mutt_debug(LL_DEBUG1, "trying rename\n");
408  if (rename(src, target) == -1)
409  {
410  mutt_debug(LL_DEBUG1, "rename (%s, %s) failed: %s (%d)\n", src, target,
411  strerror(errno), errno);
412  return -1;
413  }
414  mutt_debug(LL_DEBUG1, "rename succeeded\n");
415 
416  return 0;
417  }
418 
419  return -1;
420  }
421 
422  /* Remove the compare_stat() check, because it causes problems with maildir
423  * on filesystems that don't properly support hard links, such as sshfs. The
424  * filesystem creates the link, but the resulting file is given a different
425  * inode number by the sshfs layer. This results in an infinite loop
426  * creating links. */
427 #if 0
428  /* Stat both links and check if they are equal. */
429  if (lstat(src, &ssb) == -1)
430  {
431  mutt_debug(LL_DEBUG1, "#1 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
432  return -1;
433  }
434 
435  if (lstat(target, &tsb) == -1)
436  {
437  mutt_debug(LL_DEBUG1, "#2 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
438  return -1;
439  }
440 
441  /* pretend that the link failed because the target file did already exist. */
442 
443  if (!compare_stat(&ssb, &tsb))
444  {
445  mutt_debug(LL_DEBUG1, "stat blocks for %s and %s diverge; pretending EEXIST\n", src, target);
446  errno = EEXIST;
447  return -1;
448  }
449 #endif
450 
451 success:
452  /* Unlink the original link.
453  * Should we really ignore the return value here? XXX */
454  if (unlink(src) == -1)
455  {
456  mutt_debug(LL_DEBUG1, "unlink (%s) failed: %s (%d)\n", src, strerror(errno), errno);
457  }
458 
459  return 0;
460 }
461 
468 int mutt_file_rmtree(const char *path)
469 {
470  if (!path)
471  return -1;
472 
473  struct dirent *de = NULL;
474  struct stat statbuf;
475  int rc = 0;
476 
477  DIR *dirp = opendir(path);
478  if (!dirp)
479  {
480  mutt_debug(LL_DEBUG1, "error opening directory %s\n", path);
481  return -1;
482  }
483 
484  /* We avoid using the buffer pool for this function, because it
485  * invokes recursively to an unknown depth. */
486  struct Buffer cur = mutt_buffer_make(PATH_MAX);
487 
488  while ((de = readdir(dirp)))
489  {
490  if ((strcmp(".", de->d_name) == 0) || (strcmp("..", de->d_name) == 0))
491  continue;
492 
493  mutt_buffer_printf(&cur, "%s/%s", path, de->d_name);
494  /* XXX make nonrecursive version */
495 
496  if (stat(mutt_buffer_string(&cur), &statbuf) == -1)
497  {
498  rc = 1;
499  continue;
500  }
501 
502  if (S_ISDIR(statbuf.st_mode))
504  else
505  rc |= unlink(mutt_buffer_string(&cur));
506  }
507  closedir(dirp);
508 
509  rc |= rmdir(path);
510 
511  mutt_buffer_dealloc(&cur);
512  return rc;
513 }
514 
522 int mutt_file_open(const char *path, uint32_t flags)
523 {
524  if (!path)
525  return -1;
526 
527  struct stat osb, nsb;
528  int fd;
529  struct Buffer safe_file = mutt_buffer_make(0);
530  struct Buffer safe_dir = mutt_buffer_make(0);
531 
532  if (flags & O_EXCL)
533  {
534  mutt_buffer_alloc(&safe_file, PATH_MAX);
535  mutt_buffer_alloc(&safe_dir, PATH_MAX);
536 
537  if (mkwrapdir(path, &safe_file, &safe_dir) == -1)
538  {
539  fd = -1;
540  goto cleanup;
541  }
542 
543  fd = open(mutt_buffer_string(&safe_file), flags, 0600);
544  if (fd < 0)
545  {
546  rmdir(mutt_buffer_string(&safe_dir));
547  goto cleanup;
548  }
549 
550  /* NFS and I believe cygwin do not handle movement of open files well */
551  close(fd);
552  if (put_file_in_place(path, mutt_buffer_string(&safe_file),
553  mutt_buffer_string(&safe_dir)) == -1)
554  {
555  fd = -1;
556  goto cleanup;
557  }
558  }
559 
560  fd = open(path, flags & ~O_EXCL, 0600);
561  if (fd < 0)
562  goto cleanup;
563 
564  /* make sure the file is not symlink */
565  if (((lstat(path, &osb) < 0) || (fstat(fd, &nsb) < 0)) || !compare_stat(&osb, &nsb))
566  {
567  close(fd);
568  fd = -1;
569  goto cleanup;
570  }
571 
572 cleanup:
573  mutt_buffer_dealloc(&safe_file);
574  mutt_buffer_dealloc(&safe_dir);
575 
576  return fd;
577 }
578 
589 FILE *mutt_file_fopen(const char *path, const char *mode)
590 {
591  if (!path || !mode)
592  return NULL;
593 
594  if (mode[0] == 'w')
595  {
596  uint32_t flags = O_CREAT | O_EXCL | O_NOFOLLOW;
597 
598  if (mode[1] == '+')
599  flags |= O_RDWR;
600  else
601  flags |= O_WRONLY;
602 
603  int fd = mutt_file_open(path, flags);
604  if (fd < 0)
605  return NULL;
606 
607  return fdopen(fd, mode);
608  }
609  else
610  return fopen(path, mode);
611 }
612 
618 void mutt_file_sanitize_filename(char *path, bool slash)
619 {
620  if (!path)
621  return;
622 
623  for (; *path; path++)
624  {
625  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
626  *path = '_';
627  }
628 }
629 
637 int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
638 {
639  if (!dest || !src)
640  return -1;
641 
642  mutt_buffer_reset(dest);
643  while (*src != '\0')
644  {
645  if (strchr(rx_special_chars, *src))
646  mutt_buffer_addch(dest, '\\');
647  mutt_buffer_addch(dest, *src++);
648  }
649 
650  return 0;
651 }
652 
667 char *mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
668 {
669  if (!size || !fp)
670  return NULL;
671 
672  size_t offset = 0;
673  char *ch = NULL;
674 
675  if (!line)
676  {
677  *size = 256;
678  line = mutt_mem_malloc(*size);
679  }
680 
681  while (true)
682  {
683  if (!fgets(line + offset, *size - offset, fp))
684  {
685  FREE(&line);
686  return NULL;
687  }
688  ch = strchr(line + offset, '\n');
689  if (ch)
690  {
691  if (line_num)
692  (*line_num)++;
693  if (flags & MUTT_RL_EOL)
694  return line;
695  *ch = '\0';
696  if ((ch > line) && (*(ch - 1) == '\r'))
697  *--ch = '\0';
698  if (!(flags & MUTT_RL_CONT) || (ch == line) || (*(ch - 1) != '\\'))
699  return line;
700  offset = ch - line - 1;
701  }
702  else
703  {
704  int c;
705  c = getc(fp); /* This is kind of a hack. We want to know if the
706  char at the current point in the input stream is EOF.
707  feof() will only tell us if we've already hit EOF, not
708  if the next character is EOF. So, we need to read in
709  the next character and manually check if it is EOF. */
710  if (c == EOF)
711  {
712  /* The last line of fp isn't \n terminated */
713  if (line_num)
714  (*line_num)++;
715  return line;
716  }
717  else
718  {
719  ungetc(c, fp); /* undo our damage */
720  /* There wasn't room for the line -- increase "line" */
721  offset = *size - 1; /* overwrite the terminating 0 */
722  *size += 256;
723  mutt_mem_realloc(&line, *size);
724  }
725  }
726  }
727 }
728 
748 bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, ReadLineFlags flags)
749 {
750  if (!iter)
751  return false;
752 
753  char *p = mutt_file_read_line(iter->line, &iter->size, fp, &iter->line_num, flags);
754  if (!p)
755  return false;
756  iter->line = p;
757  return true;
758 }
759 
769 bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, ReadLineFlags flags)
770 {
771  if (!func || !fp)
772  return false;
773 
774  struct MuttFileIter iter = { 0 };
775  while (mutt_file_iter_line(&iter, fp, flags))
776  {
777  if (!(*func)(iter.line, iter.line_num, user_data))
778  {
779  FREE(&iter.line);
780  return false;
781  }
782  }
783  return true;
784 }
785 
795 size_t mutt_file_quote_filename(const char *filename, char *buf, size_t buflen)
796 {
797  if (!buf)
798  return 0;
799 
800  if (!filename)
801  {
802  *buf = '\0';
803  return 0;
804  }
805 
806  size_t j = 0;
807 
808  /* leave some space for the trailing characters. */
809  buflen -= 6;
810 
811  buf[j++] = '\'';
812 
813  for (size_t i = 0; (j < buflen) && filename[i]; i++)
814  {
815  if ((filename[i] == '\'') || (filename[i] == '`'))
816  {
817  buf[j++] = '\'';
818  buf[j++] = '\\';
819  buf[j++] = filename[i];
820  buf[j++] = '\'';
821  }
822  else
823  buf[j++] = filename[i];
824  }
825 
826  buf[j++] = '\'';
827  buf[j] = '\0';
828 
829  return j;
830 }
831 
838 void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
839 {
840  if (!buf || !filename)
841  return;
842 
843  mutt_buffer_reset(buf);
844  if (add_outer)
845  mutt_buffer_addch(buf, '\'');
846 
847  for (; *filename != '\0'; filename++)
848  {
849  if ((*filename == '\'') || (*filename == '`'))
850  {
851  mutt_buffer_addch(buf, '\'');
852  mutt_buffer_addch(buf, '\\');
853  mutt_buffer_addch(buf, *filename);
854  mutt_buffer_addch(buf, '\'');
855  }
856  else
857  mutt_buffer_addch(buf, *filename);
858  }
859 
860  if (add_outer)
861  mutt_buffer_addch(buf, '\'');
862 }
863 
877 int mutt_file_mkdir(const char *path, mode_t mode)
878 {
879  if (!path || (*path == '\0'))
880  {
881  errno = EINVAL;
882  return -1;
883  }
884 
885  errno = 0;
886  char tmp_path[PATH_MAX];
887  const size_t len = strlen(path);
888 
889  if (len >= sizeof(tmp_path))
890  {
891  errno = ENAMETOOLONG;
892  return -1;
893  }
894 
895  struct stat sb;
896  if ((stat(path, &sb) == 0) && S_ISDIR(sb.st_mode))
897  return 0;
898 
899  /* Create a mutable copy */
900  mutt_str_copy(tmp_path, path, sizeof(tmp_path));
901 
902  for (char *p = tmp_path + 1; *p; p++)
903  {
904  if (*p != '/')
905  continue;
906 
907  /* Temporarily truncate the path */
908  *p = '\0';
909 
910  if ((mkdir(tmp_path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) && (errno != EEXIST))
911  return -1;
912 
913  *p = '/';
914  }
915 
916  if ((mkdir(tmp_path, mode) != 0) && (errno != EEXIST))
917  return -1;
918 
919  return 0;
920 }
921 
932 FILE *mutt_file_mkstemp_full(const char *file, int line, const char *func)
933 {
934  char name[PATH_MAX];
935 
936  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
937  int n = snprintf(name, sizeof(name), "%s/neomutt-XXXXXX", NONULL(c_tmpdir));
938  if (n < 0)
939  return NULL;
940 
941  int fd = mkstemp(name);
942  if (fd == -1)
943  return NULL;
944 
945  FILE *fp = fdopen(fd, "w+");
946 
947  if ((unlink(name) != 0) && (errno != ENOENT))
948  {
949  mutt_file_fclose(&fp);
950  return NULL;
951  }
952 
953  MuttLogger(0, file, line, func, 1, "created temp file '%s'\n", name);
954  return fp;
955 }
956 
966 time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
967 {
968  if (!fp)
969  return -1;
970 
971  struct utimbuf utim;
972  struct stat st2;
973  time_t mtime;
974 
975  if (!st)
976  {
977  if (stat(fp, &st2) == -1)
978  return -1;
979  st = &st2;
980  }
981 
982  mtime = st->st_mtime;
983  if (mtime == mutt_date_epoch())
984  {
985  mtime -= 1;
986  utim.actime = mtime;
987  utim.modtime = mtime;
988  int rc;
989  do
990  {
991  rc = utime(fp, &utim);
992  } while ((rc == -1) && (errno == EINTR));
993 
994  if (rc == -1)
995  return -1;
996  }
997 
998  return mtime;
999 }
1000 
1006 void mutt_file_set_mtime(const char *from, const char *to)
1007 {
1008  if (!from || !to)
1009  return;
1010 
1011  struct utimbuf utim;
1012  struct stat st;
1013 
1014  if (stat(from, &st) != -1)
1015  {
1016  utim.actime = st.st_mtime;
1017  utim.modtime = st.st_mtime;
1018  utime(to, &utim);
1019  }
1020 }
1021 
1030 {
1031 #ifdef HAVE_FUTIMENS
1032  struct timespec times[2] = { { 0, UTIME_NOW }, { 0, UTIME_OMIT } };
1033  futimens(fd, times);
1034 #endif
1035 }
1036 
1045 int mutt_file_chmod(const char *path, mode_t mode)
1046 {
1047  if (!path)
1048  return -1;
1049 
1050  return chmod(path, mode);
1051 }
1052 
1070 int mutt_file_chmod_add(const char *path, mode_t mode)
1071 {
1072  return mutt_file_chmod_add_stat(path, mode, NULL);
1073 }
1074 
1093 int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
1094 {
1095  if (!path)
1096  return -1;
1097 
1098  struct stat st2;
1099 
1100  if (!st)
1101  {
1102  if (stat(path, &st2) == -1)
1103  return -1;
1104  st = &st2;
1105  }
1106  return chmod(path, st->st_mode | mode);
1107 }
1108 
1126 int mutt_file_chmod_rm(const char *path, mode_t mode)
1127 {
1128  return mutt_file_chmod_rm_stat(path, mode, NULL);
1129 }
1130 
1149 int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
1150 {
1151  if (!path)
1152  return -1;
1153 
1154  struct stat st2;
1155 
1156  if (!st)
1157  {
1158  if (stat(path, &st2) == -1)
1159  return -1;
1160  st = &st2;
1161  }
1162  return chmod(path, st->st_mode & ~mode);
1163 }
1164 
1165 #if defined(USE_FCNTL)
1166 
1178 int mutt_file_lock(int fd, bool excl, bool timeout)
1179 {
1180  struct stat sb = { 0 }, prev_sb = { 0 };
1181  int count = 0;
1182  int attempt = 0;
1183 
1184  struct flock lck;
1185  memset(&lck, 0, sizeof(struct flock));
1186  lck.l_type = excl ? F_WRLCK : F_RDLCK;
1187  lck.l_whence = SEEK_SET;
1188 
1189  while (fcntl(fd, F_SETLK, &lck) == -1)
1190  {
1191  mutt_debug(LL_DEBUG1, "fcntl errno %d\n", errno);
1192  if ((errno != EAGAIN) && (errno != EACCES))
1193  {
1194  mutt_perror("fcntl");
1195  return -1;
1196  }
1197 
1198  if (fstat(fd, &sb) != 0)
1199  sb.st_size = 0;
1200 
1201  if (count == 0)
1202  prev_sb = sb;
1203 
1204  /* only unlock file if it is unchanged */
1205  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1206  {
1207  if (timeout)
1208  mutt_error(_("Timeout exceeded while attempting fcntl lock"));
1209  return -1;
1210  }
1211 
1212  prev_sb = sb;
1213 
1214  mutt_message(_("Waiting for fcntl lock... %d"), ++attempt);
1215  sleep(1);
1216  }
1217 
1218  return 0;
1219 }
1220 
1226 int mutt_file_unlock(int fd)
1227 {
1228  struct flock unlockit;
1229 
1230  memset(&unlockit, 0, sizeof(struct flock));
1231  unlockit.l_type = F_UNLCK;
1232  unlockit.l_whence = SEEK_SET;
1233  fcntl(fd, F_SETLK, &unlockit);
1234 
1235  return 0;
1236 }
1237 #elif defined(USE_FLOCK)
1238 
1250 int mutt_file_lock(int fd, bool excl, bool timeout)
1251 {
1252  struct stat sb = { 0 }, prev_sb = { 0 };
1253  int rc = 0;
1254  int count = 0;
1255  int attempt = 0;
1256 
1257  while (flock(fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
1258  {
1259  if (errno != EWOULDBLOCK)
1260  {
1261  mutt_perror("flock");
1262  rc = -1;
1263  break;
1264  }
1265 
1266  if (fstat(fd, &sb) != 0)
1267  sb.st_size = 0;
1268 
1269  if (count == 0)
1270  prev_sb = sb;
1271 
1272  /* only unlock file if it is unchanged */
1273  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1274  {
1275  if (timeout)
1276  mutt_error(_("Timeout exceeded while attempting flock lock"));
1277  rc = -1;
1278  break;
1279  }
1280 
1281  prev_sb = sb;
1282 
1283  mutt_message(_("Waiting for flock attempt... %d"), ++attempt);
1284  sleep(1);
1285  }
1286 
1287  /* release any other locks obtained in this routine */
1288  if (rc != 0)
1289  {
1290  flock(fd, LOCK_UN);
1291  }
1292 
1293  return rc;
1294 }
1295 
1301 int mutt_file_unlock(int fd)
1302 {
1303  flock(fd, LOCK_UN);
1304  return 0;
1305 }
1306 #else
1307 #error "You must select a locking mechanism via USE_FCNTL or USE_FLOCK"
1308 #endif
1309 
1314 void mutt_file_unlink_empty(const char *path)
1315 {
1316  if (!path)
1317  return;
1318 
1319  struct stat sb;
1320 
1321  int fd = open(path, O_RDWR);
1322  if (fd == -1)
1323  return;
1324 
1325  if (mutt_file_lock(fd, true, true) == -1)
1326  {
1327  close(fd);
1328  return;
1329  }
1330 
1331  if ((fstat(fd, &sb) == 0) && (sb.st_size == 0))
1332  unlink(path);
1333 
1334  mutt_file_unlock(fd);
1335  close(fd);
1336 }
1337 
1350 int mutt_file_rename(const char *oldfile, const char *newfile)
1351 {
1352  if (!oldfile || !newfile)
1353  return -1;
1354  if (access(oldfile, F_OK) != 0)
1355  return 1;
1356  if (access(newfile, F_OK) == 0)
1357  return 2;
1358 
1359  FILE *fp_old = fopen(oldfile, "r");
1360  if (!fp_old)
1361  return 3;
1362  FILE *fp_new = mutt_file_fopen(newfile, "w");
1363  if (!fp_new)
1364  {
1365  mutt_file_fclose(&fp_old);
1366  return 3;
1367  }
1368  mutt_file_copy_stream(fp_old, fp_new);
1369  mutt_file_fclose(&fp_new);
1370  mutt_file_fclose(&fp_old);
1371  mutt_file_unlink(oldfile);
1372  return 0;
1373 }
1374 
1385 char *mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
1386 {
1387  FILE *fp = mutt_file_fopen(file, "r");
1388  if (!fp)
1389  return NULL;
1390 
1391  buf = fgets(buf, buflen, fp);
1392  mutt_file_fclose(&fp);
1393 
1394  if (!buf)
1395  return NULL;
1396 
1397  SKIPWS(buf);
1398  char *start = buf;
1399 
1400  while ((*buf != '\0') && !isspace(*buf))
1401  buf++;
1402 
1403  *buf = '\0';
1404 
1405  return start;
1406 }
1407 
1415 int mutt_file_check_empty(const char *path)
1416 {
1417  if (!path)
1418  return -1;
1419 
1420  struct stat st;
1421  if (stat(path, &st) == -1)
1422  return -1;
1423 
1424  return st.st_size == 0;
1425 }
1426 
1435 void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
1436 {
1437  struct Buffer tmp = mutt_buffer_make(PATH_MAX);
1438 
1439  mutt_buffer_quote_filename(&tmp, src, true);
1440  mutt_file_expand_fmt(dest, fmt, mutt_buffer_string(&tmp));
1441  mutt_buffer_dealloc(&tmp);
1442 }
1443 
1450 void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
1451 {
1452  if (!dest || !fmt || !src)
1453  return;
1454 
1455  const char *p = NULL;
1456  bool found = false;
1457 
1458  mutt_buffer_reset(dest);
1459 
1460  for (p = fmt; *p; p++)
1461  {
1462  if (*p == '%')
1463  {
1464  switch (p[1])
1465  {
1466  case '%':
1467  mutt_buffer_addch(dest, *p++);
1468  break;
1469  case 's':
1470  found = true;
1471  mutt_buffer_addstr(dest, src);
1472  p++;
1473  break;
1474  default:
1475  mutt_buffer_addch(dest, *p);
1476  break;
1477  }
1478  }
1479  else
1480  {
1481  mutt_buffer_addch(dest, *p);
1482  }
1483  }
1484 
1485  if (!found)
1486  {
1487  mutt_buffer_addch(dest, ' ');
1488  mutt_buffer_addstr(dest, src);
1489  }
1490 }
1491 
1498 long mutt_file_get_size(const char *path)
1499 {
1500  if (!path)
1501  return 0;
1502 
1503  struct stat sb;
1504  if (stat(path, &sb) != 0)
1505  return 0;
1506 
1507  return sb.st_size;
1508 }
1509 
1519 {
1520  if (!a || !b)
1521  return 0;
1522  if (a->tv_sec < b->tv_sec)
1523  return -1;
1524  if (a->tv_sec > b->tv_sec)
1525  return 1;
1526 
1527  if (a->tv_nsec < b->tv_nsec)
1528  return -1;
1529  if (a->tv_nsec > b->tv_nsec)
1530  return 1;
1531  return 0;
1532 }
1533 
1540 void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
1541 {
1542  if (!dest || !sb)
1543  return;
1544 
1545  dest->tv_sec = 0;
1546  dest->tv_nsec = 0;
1547 
1548  switch (type)
1549  {
1550  case MUTT_STAT_ATIME:
1551  dest->tv_sec = sb->st_atime;
1552 #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
1553  dest->tv_nsec = sb->st_atim.tv_nsec;
1554 #endif
1555  break;
1556  case MUTT_STAT_MTIME:
1557  dest->tv_sec = sb->st_mtime;
1558 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1559  dest->tv_nsec = sb->st_mtim.tv_nsec;
1560 #endif
1561  break;
1562  case MUTT_STAT_CTIME:
1563  dest->tv_sec = sb->st_ctime;
1564 #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1565  dest->tv_nsec = sb->st_ctim.tv_nsec;
1566 #endif
1567  break;
1568  }
1569 }
1570 
1580 int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type,
1581  struct timespec *b)
1582 {
1583  if (!sba || !b)
1584  return 0;
1585 
1586  struct timespec a = { 0 };
1587 
1588  mutt_file_get_stat_timespec(&a, sba, type);
1589  return mutt_file_timespec_compare(&a, b);
1590 }
1591 
1602 int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type,
1603  struct stat *sbb, enum MuttStatType sbb_type)
1604 {
1605  if (!sba || !sbb)
1606  return 0;
1607 
1608  struct timespec a = { 0 };
1609  struct timespec b = { 0 };
1610 
1611  mutt_file_get_stat_timespec(&a, sba, sba_type);
1612  mutt_file_get_stat_timespec(&b, sbb, sbb_type);
1613  return mutt_file_timespec_compare(&a, &b);
1614 }
1615 
1621 {
1622  struct stat st;
1623  int rc = lstat(mutt_buffer_string(buf), &st);
1624  if ((rc != -1) && S_ISLNK(st.st_mode))
1625  {
1626  char path[PATH_MAX];
1627  if (realpath(mutt_buffer_string(buf), path))
1628  {
1629  mutt_buffer_strcpy(buf, path);
1630  }
1631  }
1632 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
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:838
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1518
#define MAX_LOCK_ATTEMPTS
Definition: file.c:64
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1029
#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
Memory management wrappers.
long mutt_file_get_size(const char *path)
Get the size of a file.
Definition: file.c:1498
File/dir&#39;s ctime - creation time.
Definition: file.h:64
char * line
the line data
Definition: file.h:72
#define MUTT_RL_CONT
-continuation
Definition: file.h:39
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:1006
#define mutt_error(...)
Definition: logging.h:88
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:169
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1350
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:1540
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1149
bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, ReadLineFlags flags)
iterate over the lines from an open file pointer
Definition: file.c:748
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:195
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
log_dispatcher_t MuttLogger
The log dispatcher -This function pointer controls where log messages are redirected.
Definition: logging.c:52
int mutt_file_rmtree(const char *path)
Recursively remove a directory.
Definition: file.c:468
String manipulation buffer.
Definition: buffer.h:33
#define _(a)
Definition: message.h:28
static bool compare_stat(struct stat *osb, struct stat *nsb)
Compare the struct stat&#39;s of two files/dirs.
Definition: file.c:80
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:563
int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
Add permissions to a file.
Definition: file.c:1093
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:667
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
File management functions.
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1226
int mutt_file_chmod_rm(const char *path, mode_t mode)
Remove permissions from a file.
Definition: file.c:1126
Logging Dispatcher.
Convenience wrapper for the config headers.
time_t tv_sec
Definition: file.h:50
String manipulation functions.
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define MUTT_RL_EOL
don&#39;t strip \n / \r\n
Definition: file.h:40
int mutt_file_lock(int fd, bool excl, bool timeout)
(try to) lock a file using fcntl()
Definition: file.c:1178
void mutt_file_unlink_empty(const char *path)
Delete a file if it&#39;s empty.
Definition: file.c:1314
static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
Create a temporary directory next to a file name.
Definition: file.c:94
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:877
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
Convenience wrapper for the core headers.
MuttStatType
Flags for mutt_file_get_stat_timespec.
Definition: file.h:60
#define SKIPWS(ch)
Definition: string2.h:46
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
File/dir&#39;s mtime - last modified time.
Definition: file.h:63
int mutt_file_chmod(const char *path, mode_t mode)
Set permissions of a file.
Definition: file.c:1045
long tv_nsec
Definition: file.h:51
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:522
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
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:618
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:637
uint8_t ReadLineFlags
Flags for mutt_file_read_line(), e.g. MUTT_RL_CONT.
Definition: file.h:37
#define PATH_MAX
Definition: mutt.h:40
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
int line_num
line number
Definition: file.h:74
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:1450
static const char rx_special_chars[]
Definition: file.c:59
char * data
Pointer to data.
Definition: buffer.h:35
size_t size
allocated size of line data
Definition: file.h:73
void mutt_file_resolve_symlink(struct Buffer *buf)
Resolve a symlink in place.
Definition: file.c:1620
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:795
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:241
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
State record for mutt_file_iter_line()
Definition: file.h:70
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:1580
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
char * mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
Read a keyword from a file.
Definition: file.c:1385
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:1602
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:137
bool(* mutt_file_map_t)(char *line, int line_num, void *user_data)
Callback function for mutt_file_map_lines()
Definition: file.h:85
General purpose object for storing and parsing strings.
Log at debug level 1.
Definition: logging.h:40
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
#define O_NOFOLLOW
Definition: file.c:68
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1415
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:1435
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:966
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
Time value with nanosecond precision.
Definition: file.h:48
const char filename_safe_chars[]
Definition: file.c:61
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1070
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:299
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
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:932
File/dir&#39;s atime - last accessed time.
Definition: file.h:62
bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, ReadLineFlags flags)
Process lines of text read from a file pointer.
Definition: file.c:769
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:354