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