NeoMutt  2020-06-26-89-g172cd3
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 "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 #ifdef USE_FLOCK
53 #include <sys/file.h>
54 #endif
55 
56 char *C_Tmpdir;
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_b2s(&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_b2s(&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_b2s(&cur), &statbuf) == -1)
497  {
498  rc = 1;
499  continue;
500  }
501 
502  if (S_ISDIR(statbuf.st_mode))
503  rc |= mutt_file_rmtree(mutt_b2s(&cur));
504  else
505  rc |= unlink(mutt_b2s(&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, int 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_b2s(&safe_file), flags, 0600);
544  if (fd < 0)
545  {
546  rmdir(mutt_b2s(&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_b2s(&safe_file), mutt_b2s(&safe_dir)) == -1)
553  {
554  fd = -1;
555  goto cleanup;
556  }
557  }
558 
559  fd = open(path, flags & ~O_EXCL, 0600);
560  if (fd < 0)
561  goto cleanup;
562 
563  /* make sure the file is not symlink */
564  if (((lstat(path, &osb) < 0) || (fstat(fd, &nsb) < 0)) || !compare_stat(&osb, &nsb))
565  {
566  close(fd);
567  fd = -1;
568  goto cleanup;
569  }
570 
571 cleanup:
572  mutt_buffer_dealloc(&safe_file);
573  mutt_buffer_dealloc(&safe_dir);
574 
575  return fd;
576 }
577 
588 FILE *mutt_file_fopen(const char *path, const char *mode)
589 {
590  if (!path || !mode)
591  return NULL;
592 
593  if (mode[0] == 'w')
594  {
595  int fd;
596  int flags = O_CREAT | O_EXCL | O_NOFOLLOW;
597 
598  if (mode[1] == '+')
599  flags |= O_RDWR;
600  else
601  flags |= O_WRONLY;
602 
603  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, int 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_EOL)
694  return line;
695  *ch = '\0';
696  if ((ch > line) && (*(ch - 1) == '\r'))
697  *--ch = '\0';
698  if (!(flags & MUTT_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 
747 bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, int flags)
748 {
749  if (!iter)
750  return false;
751 
752  char *p = mutt_file_read_line(iter->line, &iter->size, fp, &iter->line_num, flags);
753  if (!p)
754  return false;
755  iter->line = p;
756  return true;
757 }
758 
767 bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, int flags)
768 {
769  if (!func || !fp)
770  return false;
771 
772  struct MuttFileIter iter = { 0 };
773  while (mutt_file_iter_line(&iter, fp, flags))
774  {
775  if (!(*func)(iter.line, iter.line_num, user_data))
776  {
777  FREE(&iter.line);
778  return false;
779  }
780  }
781  return true;
782 }
783 
793 size_t mutt_file_quote_filename(const char *filename, char *buf, size_t buflen)
794 {
795  if (!buf)
796  return 0;
797 
798  if (!filename)
799  {
800  *buf = '\0';
801  return 0;
802  }
803 
804  size_t j = 0;
805 
806  /* leave some space for the trailing characters. */
807  buflen -= 6;
808 
809  buf[j++] = '\'';
810 
811  for (size_t i = 0; (j < buflen) && filename[i]; i++)
812  {
813  if ((filename[i] == '\'') || (filename[i] == '`'))
814  {
815  buf[j++] = '\'';
816  buf[j++] = '\\';
817  buf[j++] = filename[i];
818  buf[j++] = '\'';
819  }
820  else
821  buf[j++] = filename[i];
822  }
823 
824  buf[j++] = '\'';
825  buf[j] = '\0';
826 
827  return j;
828 }
829 
836 void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
837 {
838  if (!buf || !filename)
839  return;
840 
841  mutt_buffer_reset(buf);
842  if (add_outer)
843  mutt_buffer_addch(buf, '\'');
844 
845  for (; *filename != '\0'; filename++)
846  {
847  if ((*filename == '\'') || (*filename == '`'))
848  {
849  mutt_buffer_addch(buf, '\'');
850  mutt_buffer_addch(buf, '\\');
851  mutt_buffer_addch(buf, *filename);
852  mutt_buffer_addch(buf, '\'');
853  }
854  else
855  mutt_buffer_addch(buf, *filename);
856  }
857 
858  if (add_outer)
859  mutt_buffer_addch(buf, '\'');
860 }
861 
875 int mutt_file_mkdir(const char *path, mode_t mode)
876 {
877  if (!path || (*path == '\0'))
878  {
879  errno = EINVAL;
880  return -1;
881  }
882 
883  errno = 0;
884  char tmp_path[PATH_MAX];
885  const size_t len = strlen(path);
886 
887  if (len >= sizeof(tmp_path))
888  {
889  errno = ENAMETOOLONG;
890  return -1;
891  }
892 
893  struct stat sb;
894  if ((stat(path, &sb) == 0) && S_ISDIR(sb.st_mode))
895  return 0;
896 
897  /* Create a mutable copy */
898  mutt_str_copy(tmp_path, path, sizeof(tmp_path));
899 
900  for (char *p = tmp_path + 1; *p; p++)
901  {
902  if (*p != '/')
903  continue;
904 
905  /* Temporarily truncate the path */
906  *p = '\0';
907 
908  if ((mkdir(tmp_path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) && (errno != EEXIST))
909  return -1;
910 
911  *p = '/';
912  }
913 
914  if ((mkdir(tmp_path, mode) != 0) && (errno != EEXIST))
915  return -1;
916 
917  return 0;
918 }
919 
930 FILE *mutt_file_mkstemp_full(const char *file, int line, const char *func)
931 {
932  char name[PATH_MAX];
933 
934  int n = snprintf(name, sizeof(name), "%s/neomutt-XXXXXX", NONULL(C_Tmpdir));
935  if (n < 0)
936  return NULL;
937 
938  int fd = mkstemp(name);
939  if (fd == -1)
940  return NULL;
941 
942  FILE *fp = fdopen(fd, "w+");
943 
944  if ((unlink(name) != 0) && (errno != ENOENT))
945  {
946  mutt_file_fclose(&fp);
947  return NULL;
948  }
949 
950  MuttLogger(0, file, line, func, 1, "created temp file '%s'\n", name);
951  return fp;
952 }
953 
963 time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
964 {
965  if (!fp)
966  return -1;
967 
968  struct utimbuf utim;
969  struct stat st2;
970  time_t mtime;
971 
972  if (!st)
973  {
974  if (stat(fp, &st2) == -1)
975  return -1;
976  st = &st2;
977  }
978 
979  mtime = st->st_mtime;
980  if (mtime == mutt_date_epoch())
981  {
982  mtime -= 1;
983  utim.actime = mtime;
984  utim.modtime = mtime;
985  utime(fp, &utim);
986  }
987 
988  return mtime;
989 }
990 
996 void mutt_file_set_mtime(const char *from, const char *to)
997 {
998  if (!from || !to)
999  return;
1000 
1001  struct utimbuf utim;
1002  struct stat st;
1003 
1004  if (stat(from, &st) != -1)
1005  {
1006  utim.actime = st.st_mtime;
1007  utim.modtime = st.st_mtime;
1008  utime(to, &utim);
1009  }
1010 }
1011 
1020 {
1021 #ifdef HAVE_FUTIMENS
1022  struct timespec times[2] = { { 0, UTIME_NOW }, { 0, UTIME_OMIT } };
1023  futimens(fd, times);
1024 #endif
1025 }
1026 
1035 int mutt_file_chmod(const char *path, mode_t mode)
1036 {
1037  if (!path)
1038  return -1;
1039 
1040  return chmod(path, mode);
1041 }
1042 
1060 int mutt_file_chmod_add(const char *path, mode_t mode)
1061 {
1062  return mutt_file_chmod_add_stat(path, mode, NULL);
1063 }
1064 
1083 int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
1084 {
1085  if (!path)
1086  return -1;
1087 
1088  struct stat st2;
1089 
1090  if (!st)
1091  {
1092  if (stat(path, &st2) == -1)
1093  return -1;
1094  st = &st2;
1095  }
1096  return chmod(path, st->st_mode | mode);
1097 }
1098 
1116 int mutt_file_chmod_rm(const char *path, mode_t mode)
1117 {
1118  return mutt_file_chmod_rm_stat(path, mode, NULL);
1119 }
1120 
1139 int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
1140 {
1141  if (!path)
1142  return -1;
1143 
1144  struct stat st2;
1145 
1146  if (!st)
1147  {
1148  if (stat(path, &st2) == -1)
1149  return -1;
1150  st = &st2;
1151  }
1152  return chmod(path, st->st_mode & ~mode);
1153 }
1154 
1155 #if defined(USE_FCNTL)
1156 
1168 int mutt_file_lock(int fd, bool excl, bool timeout)
1169 {
1170  struct stat sb = { 0 }, prev_sb = { 0 };
1171  int count = 0;
1172  int attempt = 0;
1173 
1174  struct flock lck;
1175  memset(&lck, 0, sizeof(struct flock));
1176  lck.l_type = excl ? F_WRLCK : F_RDLCK;
1177  lck.l_whence = SEEK_SET;
1178 
1179  while (fcntl(fd, F_SETLK, &lck) == -1)
1180  {
1181  mutt_debug(LL_DEBUG1, "fcntl errno %d\n", errno);
1182  if ((errno != EAGAIN) && (errno != EACCES))
1183  {
1184  mutt_perror("fcntl");
1185  return -1;
1186  }
1187 
1188  if (fstat(fd, &sb) != 0)
1189  sb.st_size = 0;
1190 
1191  if (count == 0)
1192  prev_sb = sb;
1193 
1194  /* only unlock file if it is unchanged */
1195  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1196  {
1197  if (timeout)
1198  mutt_error(_("Timeout exceeded while attempting fcntl lock"));
1199  return -1;
1200  }
1201 
1202  prev_sb = sb;
1203 
1204  mutt_message(_("Waiting for fcntl lock... %d"), ++attempt);
1205  sleep(1);
1206  }
1207 
1208  return 0;
1209 }
1210 
1216 int mutt_file_unlock(int fd)
1217 {
1218  struct flock unlockit;
1219 
1220  memset(&unlockit, 0, sizeof(struct flock));
1221  unlockit.l_type = F_UNLCK;
1222  unlockit.l_whence = SEEK_SET;
1223  fcntl(fd, F_SETLK, &unlockit);
1224 
1225  return 0;
1226 }
1227 #elif defined(USE_FLOCK)
1228 
1240 int mutt_file_lock(int fd, bool excl, bool timeout)
1241 {
1242  struct stat sb = { 0 }, prev_sb = { 0 };
1243  int rc = 0;
1244  int count = 0;
1245  int attempt = 0;
1246 
1247  while (flock(fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
1248  {
1249  if (errno != EWOULDBLOCK)
1250  {
1251  mutt_perror("flock");
1252  rc = -1;
1253  break;
1254  }
1255 
1256  if (fstat(fd, &sb) != 0)
1257  sb.st_size = 0;
1258 
1259  if (count == 0)
1260  prev_sb = sb;
1261 
1262  /* only unlock file if it is unchanged */
1263  if ((prev_sb.st_size == sb.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1264  {
1265  if (timeout)
1266  mutt_error(_("Timeout exceeded while attempting flock lock"));
1267  rc = -1;
1268  break;
1269  }
1270 
1271  prev_sb = sb;
1272 
1273  mutt_message(_("Waiting for flock attempt... %d"), ++attempt);
1274  sleep(1);
1275  }
1276 
1277  /* release any other locks obtained in this routine */
1278  if (rc != 0)
1279  {
1280  flock(fd, LOCK_UN);
1281  }
1282 
1283  return rc;
1284 }
1285 
1291 int mutt_file_unlock(int fd)
1292 {
1293  flock(fd, LOCK_UN);
1294  return 0;
1295 }
1296 #else
1297 #error "You must select a locking mechanism via USE_FCNTL or USE_FLOCK"
1298 #endif
1299 
1304 void mutt_file_unlink_empty(const char *path)
1305 {
1306  if (!path)
1307  return;
1308 
1309  struct stat sb;
1310 
1311  int fd = open(path, O_RDWR);
1312  if (fd == -1)
1313  return;
1314 
1315  if (mutt_file_lock(fd, true, true) == -1)
1316  {
1317  close(fd);
1318  return;
1319  }
1320 
1321  if ((fstat(fd, &sb) == 0) && (sb.st_size == 0))
1322  unlink(path);
1323 
1324  mutt_file_unlock(fd);
1325  close(fd);
1326 }
1327 
1340 int mutt_file_rename(const char *oldfile, const char *newfile)
1341 {
1342  if (!oldfile || !newfile)
1343  return -1;
1344  if (access(oldfile, F_OK) != 0)
1345  return 1;
1346  if (access(newfile, F_OK) == 0)
1347  return 2;
1348 
1349  FILE *fp_old = fopen(oldfile, "r");
1350  if (!fp_old)
1351  return 3;
1352  FILE *fp_new = mutt_file_fopen(newfile, "w");
1353  if (!fp_new)
1354  {
1355  mutt_file_fclose(&fp_old);
1356  return 3;
1357  }
1358  mutt_file_copy_stream(fp_old, fp_new);
1359  mutt_file_fclose(&fp_new);
1360  mutt_file_fclose(&fp_old);
1361  mutt_file_unlink(oldfile);
1362  return 0;
1363 }
1364 
1375 char *mutt_file_read_keyword(const char *file, char *buf, size_t buflen)
1376 {
1377  FILE *fp = mutt_file_fopen(file, "r");
1378  if (!fp)
1379  return NULL;
1380 
1381  buf = fgets(buf, buflen, fp);
1382  mutt_file_fclose(&fp);
1383 
1384  if (!buf)
1385  return NULL;
1386 
1387  SKIPWS(buf);
1388  char *start = buf;
1389 
1390  while ((*buf != '\0') && !isspace(*buf))
1391  buf++;
1392 
1393  *buf = '\0';
1394 
1395  return start;
1396 }
1397 
1405 int mutt_file_check_empty(const char *path)
1406 {
1407  if (!path)
1408  return -1;
1409 
1410  struct stat st;
1411  if (stat(path, &st) == -1)
1412  return -1;
1413 
1414  return st.st_size == 0;
1415 }
1416 
1425 void mutt_buffer_file_expand_fmt_quote(struct Buffer *dest, const char *fmt, const char *src)
1426 {
1427  struct Buffer tmp = mutt_buffer_make(PATH_MAX);
1428 
1429  mutt_buffer_quote_filename(&tmp, src, true);
1430  mutt_file_expand_fmt(dest, fmt, mutt_b2s(&tmp));
1431  mutt_buffer_dealloc(&tmp);
1432 }
1433 
1440 void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
1441 {
1442  if (!dest || !fmt || !src)
1443  return;
1444 
1445  const char *p = NULL;
1446  bool found = false;
1447 
1448  mutt_buffer_reset(dest);
1449 
1450  for (p = fmt; *p; p++)
1451  {
1452  if (*p == '%')
1453  {
1454  switch (p[1])
1455  {
1456  case '%':
1457  mutt_buffer_addch(dest, *p++);
1458  break;
1459  case 's':
1460  found = true;
1461  mutt_buffer_addstr(dest, src);
1462  p++;
1463  break;
1464  default:
1465  mutt_buffer_addch(dest, *p);
1466  break;
1467  }
1468  }
1469  else
1470  {
1471  mutt_buffer_addch(dest, *p);
1472  }
1473  }
1474 
1475  if (!found)
1476  {
1477  mutt_buffer_addch(dest, ' ');
1478  mutt_buffer_addstr(dest, src);
1479  }
1480 }
1481 
1488 long mutt_file_get_size(const char *path)
1489 {
1490  if (!path)
1491  return 0;
1492 
1493  struct stat sb;
1494  if (stat(path, &sb) != 0)
1495  return 0;
1496 
1497  return sb.st_size;
1498 }
1499 
1509 {
1510  if (!a || !b)
1511  return 0;
1512  if (a->tv_sec < b->tv_sec)
1513  return -1;
1514  if (a->tv_sec > b->tv_sec)
1515  return 1;
1516 
1517  if (a->tv_nsec < b->tv_nsec)
1518  return -1;
1519  if (a->tv_nsec > b->tv_nsec)
1520  return 1;
1521  return 0;
1522 }
1523 
1530 void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
1531 {
1532  if (!dest || !sb)
1533  return;
1534 
1535  dest->tv_sec = 0;
1536  dest->tv_nsec = 0;
1537 
1538  switch (type)
1539  {
1540  case MUTT_STAT_ATIME:
1541  dest->tv_sec = sb->st_atime;
1542 #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
1543  dest->tv_nsec = sb->st_atim.tv_nsec;
1544 #endif
1545  break;
1546  case MUTT_STAT_MTIME:
1547  dest->tv_sec = sb->st_mtime;
1548 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1549  dest->tv_nsec = sb->st_mtim.tv_nsec;
1550 #endif
1551  break;
1552  case MUTT_STAT_CTIME:
1553  dest->tv_sec = sb->st_ctime;
1554 #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1555  dest->tv_nsec = sb->st_ctim.tv_nsec;
1556 #endif
1557  break;
1558  }
1559 }
1560 
1570 int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type,
1571  struct timespec *b)
1572 {
1573  if (!sba || !b)
1574  return 0;
1575 
1576  struct timespec a = { 0 };
1577 
1578  mutt_file_get_stat_timespec(&a, sba, type);
1579  return mutt_file_timespec_compare(&a, b);
1580 }
1581 
1592 int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type,
1593  struct stat *sbb, enum MuttStatType sbb_type)
1594 {
1595  if (!sba || !sbb)
1596  return 0;
1597 
1598  struct timespec a = { 0 };
1599  struct timespec b = { 0 };
1600 
1601  mutt_file_get_stat_timespec(&a, sba, sba_type);
1602  mutt_file_get_stat_timespec(&b, sbb, sbb_type);
1603  return mutt_file_timespec_compare(&a, &b);
1604 }
1605 
1611 {
1612  struct stat st;
1613  int rc = lstat(mutt_b2s(buf), &st);
1614  if ((rc != -1) && S_ISLNK(st.st_mode))
1615  {
1616  char path[PATH_MAX];
1617  if (realpath(mutt_b2s(buf), path))
1618  {
1619  mutt_buffer_strcpy(buf, path);
1620  }
1621  }
1622 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:414
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:836
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1508
#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:1019
#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:1488
File/dir&#39;s ctime - creation time.
Definition: file.h:62
char * line
the line data
Definition: file.h:70
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:996
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:169
#define mutt_message(...)
Definition: logging.h:83
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1340
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:1530
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1139
static int const char * fmt
Definition: acutest.h:488
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
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
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:667
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:1083
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:37
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1216
int mutt_file_chmod_rm(const char *path, mode_t mode)
Remove permissions from a file.
Definition: file.c:1116
Logging Dispatcher.
time_t tv_sec
Definition: file.h:48
String manipulation functions.
int mutt_file_lock(int fd, bool excl, bool timeout)
(try to) lock a file using fcntl()
Definition: file.c:1168
void mutt_file_unlink_empty(const char *path)
Delete a file if it&#39;s empty.
Definition: file.c:1304
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:875
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
MuttStatType
Flags for mutt_file_get_stat_timespec.
Definition: file.h:58
#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:61
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:767
int mutt_file_chmod(const char *path, mode_t mode)
Set permissions of a file.
Definition: file.c:1035
long tv_nsec
Definition: file.h:49
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:747
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
#define mutt_b2s(buf)
Definition: buffer.h:41
#define PATH_MAX
Definition: mutt.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
int line_num
line number
Definition: file.h:72
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:1440
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:71
void mutt_file_resolve_symlink(struct Buffer *buf)
Resolve a symlink in place.
Definition: file.c:1610
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:793
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
State record for mutt_file_iter_line()
Definition: file.h:68
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:1570
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:1375
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:1592
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:83
General purpose object for storing and parsing strings.
int mutt_file_open(const char *path, int flags)
Open a file.
Definition: file.c:522
Log at debug level 1.
Definition: logging.h:40
int n
Definition: acutest.h:492
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:721
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
#define mutt_error(...)
Definition: logging.h:84
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:56
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1405
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:1425
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:963
#define FREE(x)
Definition: memory.h:40
Time value with nanosecond precision.
Definition: file.h:46
#define MUTT_EOL
don&#39;t strip \n / \r\n
Definition: file.h:38
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:1060
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int const char * file
Definition: acutest.h:617
int const char int line
Definition: acutest.h:617
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:588
Time and date handling routines.
Path manipulation functions.
FILE * mutt_file_mkstemp_full(const char *file, int line, const char *func)
Create temporary file safely.
Definition: file.c:930
File/dir&#39;s atime - last accessed time.
Definition: file.h:60
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