1 #include "bsdtar_platform.h"
2
3 #include <sys/stat.h>
4 #include <sys/types.h>
5
6 #include <assert.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "asprintf.h"
15 #include "warnp.h"
16
17 #include "dirutil.h"
18
19 /**
20 * XXX Portability
21 * XXX This function should ensure that in the sequence of events
22 * XXX 1. Creation/link/unlink of a file in/to/from the directory X,
23 * XXX 2. dirutil_fsyncdir(X),
24 * XXX 3. Creation/link/unlink of a file anywhere else,
25 * XXX the system can never (even in the event of power failure) have step 3
26 * XXX take place but not step 1.
27 * XXX
28 * XXX Calling fsync on the directory X is reported to be sufficient to
29 * XXX achieve this on all widely used systems (although not necessary on
30 * XXX all of them), but this should be reviewed when porting this code.
31 */
32 /**
33 * dirutil_fsyncdir(path):
34 * Call fsync on the directory ${path}.
35 */
36 int
dirutil_fsyncdir(const char * path)37 dirutil_fsyncdir(const char * path)
38 {
39 int fd;
40
41 /* Open the directory read-only. */
42 if ((fd = open(path, O_RDONLY)) == -1) {
43 warnp("open(%s)", path);
44 return (-1);
45 }
46
47 /* Call fsync. */
48 if (fsync(fd)) {
49 warnp("fsync(%s)", path);
50 close(fd);
51 return (-1);
52 }
53
54 /* Close the descriptor. */
55 if (close(fd)) {
56 warnp("close(%s)", path);
57 return (-1);
58 }
59
60 /* Success! */
61 return (0);
62 }
63
64 /**
65 * build_dir(dir, diropt):
66 * Makes sure that ${dir} exists, creating it (and any parents) as necessary.
67 */
68 int
build_dir(const char * dir,const char * diropt)69 build_dir(const char * dir, const char * diropt)
70 {
71 struct stat sb;
72 char * s;
73 const char * dirseppos;
74
75 /* We need a directory name and the config option. */
76 assert(dir != NULL);
77 assert(diropt != NULL);
78
79 /* Move through *dir and build all parent directories. */
80 for (dirseppos = dir; *dirseppos != '\0'; ) {
81 /* Move to the next '/', or the end of the string. */
82 if ((dirseppos = strchr(dirseppos + 1, '/')) == NULL)
83 dirseppos = dir + strlen(dir);
84
85 /* Generate a string containing the parent directory. */
86 if (asprintf(&s, "%.*s", (int)(dirseppos - dir), dir) == -1) {
87 warnp("No memory");
88 goto err0;
89 }
90
91 /* Does the parent directory exist already? */
92 if (stat(s, &sb) == 0)
93 goto nextdir;
94
95 /* Did something go wrong? */
96 if (errno != ENOENT) {
97 warnp("stat(%s)", s);
98 goto err1;
99 }
100
101 /* Create the directory. */
102 if (mkdir(s, 0700)) {
103 warnp("Cannot create directory: %s", s);
104 goto err1;
105 }
106
107 /* Tell the user what we did. */
108 fprintf(stderr, "Directory %s created for \"%s %s\"\n",
109 s, diropt, dir);
110
111 nextdir:
112 free(s);
113 }
114
115 /* Make sure permissions on the directory are correct. */
116 if (stat(dir, &sb)) {
117 warnp("stat(%s)", dir);
118 goto err0;
119 }
120 if (sb.st_mode & (S_IRWXG | S_IRWXO)) {
121 if (chmod(dir, sb.st_mode & (mode_t)(~(S_IRWXG | S_IRWXO)))) {
122 warnp("Cannot sanitize permissions on directory: %s",
123 dir);
124 goto err0;
125 }
126 }
127
128 /* Success! */
129 return (0);
130
131 err1:
132 free(s);
133 err0:
134 /* Failure! */
135 return (-1);
136 }
137