File: dirutil.c

package info (click to toggle)
tarsnap 1.0.41-3
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 3,712 kB
  • sloc: ansic: 50,148; sh: 1,025; makefile: 684; python: 661; awk: 345
file content (135 lines) | stat: -rw-r--r-- 2,932 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <sys/types.h>
#include <sys/stat.h>

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "asprintf.h"
#include "warnp.h"

#include "dirutil.h"

/**
 * XXX Portability
 * XXX This function should ensure that in the sequence of events
 * XXX 1. Creation/link/unlink of a file in/to/from the directory X,
 * XXX 2. dirutil_fsyncdir(X),
 * XXX 3. Creation/link/unlink of a file anywhere else,
 * XXX the system can never (even in the event of power failure) have step 3
 * XXX take place but not step 1.
 * XXX
 * XXX Calling fsync on the directory X is reported to be sufficient to
 * XXX achieve this on all widely used systems (although not necessary on
 * XXX all of them), but this should be reviewed when porting this code.
 */
/**
 * dirutil_fsyncdir(path):
 * Call fsync on the directory ${path}.
 */
int
dirutil_fsyncdir(const char * path)
{
	int fd;

	/* Open the directory read-only. */
	if ((fd = open(path, O_RDONLY)) == -1) {
		warnp("open(%s)", path);
		return (-1);
	}

	/* Call fsync. */
	if (fsync(fd)) {
		warnp("fsync(%s)", path);
		if (close(fd))
			warnp("close");
		return (-1);
	}

	/* Close the descriptor. */
	if (close(fd)) {
		warnp("close(%s)", path);
		return (-1);
	}

	/* Success! */
	return (0);
}

/**
 * build_dir(dir, diropt):
 * Make sure that ${dir} exists, creating it (and any parents) as necessary.
 */
int
build_dir(const char * dir, const char * diropt)
{
	struct stat sb;
	char * s;
	const char * dirseppos;

	/* We need a directory name and the config option. */
	assert(dir != NULL);
	assert(diropt != NULL);

	/* Move through *dir and build all parent directories. */
	for (dirseppos = dir; *dirseppos != '\0'; ) {
		/* Move to the next '/', or the end of the string. */
		if ((dirseppos = strchr(dirseppos + 1, '/')) == NULL)
			dirseppos = dir + strlen(dir);

		/* Generate a string containing the parent directory. */
		if (asprintf(&s, "%.*s", (int)(dirseppos - dir), dir) == -1) {
			warnp("No memory");
			goto err0;
		}

		/* Does the parent directory exist already? */
		if (stat(s, &sb) == 0)
			goto nextdir;

		/* Did something go wrong? */
		if (errno != ENOENT) {
			warnp("stat(%s)", s);
			goto err1;
		}

		/* Create the directory. */
		if (mkdir(s, 0700)) {
			warnp("Cannot create directory: %s", s);
			goto err1;
		}

		/* Tell the user what we did. */
		fprintf(stderr, "Directory %s created for \"%s %s\"\n",
		    s, diropt, dir);

nextdir:
		free(s);
	}

	/* Make sure permissions on the directory are correct. */
	if (stat(dir, &sb)) {
		warnp("stat(%s)", dir);
		goto err0;
	}
	if (sb.st_mode & (S_IRWXG | S_IRWXO)) {
		if (chmod(dir, sb.st_mode & (mode_t)(~(S_IRWXG | S_IRWXO)))) {
			warnp("Cannot sanitize permissions on directory: %s",
			    dir);
			goto err0;
		}
	}

	/* Success! */
	return (0);

err1:
	free(s);
err0:
	/* Failure! */
	return (-1);
}