summaryrefslogtreecommitdiff
path: root/util.c
blob: 34667e3fc7a6ace461652ec90c285b60bdb47307 (plain)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 * Copyright (C) 2006-2009 Andre Noll <maan@tuebingen.mpg.de>
 *
 * Licensed under the GPL v2. For licencing details see COPYING.
 */

/** \file util.c Helper functions needed by both libosl and oslfsck. */

#include <sys/types.h>
#include <dirent.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/select.h>

#include "log.h"
#include "osl.h"
#include "util.h"

/**
 * Wrapper for the write system call.
 *
 * \param fd The file descriptor to write to.
 * \param buf The buffer to write.
 * \param size The length of \a buf in bytes.
 *
 * This function writes out the given buffer and retries if an interrupt
 * occurred during the write.
 *
 * \return On success, the number of bytes written is returned, otherwise, a
 * negative error code is returned.
 *
 * \sa write(2).
 */
static ssize_t __write(int fd, const void *buf, size_t size)
{
	ssize_t ret;

	for (;;) {
		ret = write(fd, buf, size);
		if ((ret < 0) && (errno == EAGAIN || errno == EINTR))
			continue;
		return ret >= 0? ret : -E_OSL_WRITE;
	}
}

/**
 * Write a buffer to a file descriptor, re-write on short writes.
 *
 * \param fd The file descriptor.
 * \param buf The buffer to be sent.
 * \param len The length of \a buf.
 *
 * \return Standard. In any case, the number of bytes that have been written is
 * stored in \a len.
 */
int write_all(int fd, const char *buf, size_t *len)
{
	size_t total = *len;

	assert(total);
	*len = 0;
	while (*len < total) {
		int ret = __write(fd, buf + *len, total - *len);
		if (ret < 0) {
			ERROR_LOG("write error: %s\n", strerror(errno));
			return ret;
		}
		*len += ret;
	}
	return 1;
}

/**
 * Wrapper for the open(2) system call.
 *
 * \param path The filename.
 * \param flags The usual open(2) flags.
 * \param mode Specifies the permissions to use.
 *
 * The mode parameter must be specified when O_CREAT is in the flags, and is
 * ignored otherwise.
 *
 * \return The file descriptor on success, negative on errors.
 *
 * \sa open(2).
 */
int osl_open(const char *path, int flags, mode_t mode)
{
	int ret = open(path, flags, mode);

	if (ret >= 0)
		return ret;
	return errno == ENOENT? -E_OSL_NOENT : -E_OSL_OPEN;
}

/**
 * Open a file, write the given buffer and close the file.
 *
 * \param filename Full path to the file to open.
 * \param buf The buffer to write to the file.
 * \param size The size of \a buf.
 *
 * \return Standard.
 */
int write_file(const char *filename, const void *buf, size_t size)
{
	int ret, fd;

	ret = osl_open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
	if (ret < 0)
		return ret;
	fd = ret;
	if (size != 0) {
		ret = write_all(fd, buf, &size);
		if (ret < 0)
			goto out;
	}
	ret = 1;
out:
	close(fd);
	return ret;
}

/**
 * Open a file and map it into memory.
 *
 * \param path Name of the regular file to map.
 * \param open_mode Either \p O_RDONLY or \p O_RDWR.
 * \param map On success, the mapping is returned here.
 * \param size size of the mapping.
 * \param fd_ptr The file descriptor of the mapping.
 *
 * If \a fd_ptr is \p NULL, the file descriptor resulting from the underlying
 * open call is closed after mmap().  Otherwise the file is kept open and the
 * file descriptor is returned in \a fd_ptr.
 *
 * \return Standard.
 *
 * \sa osl_open(), mmap(2).
 */
int mmap_full_file(const char *path, int open_mode, void **map,
		size_t *size, int *fd_ptr)
{
	int fd, ret, mmap_prot, mmap_flags;
	struct stat file_status;

	if (open_mode == O_RDONLY) {
		mmap_prot = PROT_READ;
		mmap_flags = MAP_PRIVATE;
	} else {
		mmap_prot = PROT_READ | PROT_WRITE;
		mmap_flags = MAP_SHARED;
	}
	ret = osl_open(path, open_mode, 0);
	if (ret < 0)
		return ret;
	fd = ret;
	if (fstat(fd, &file_status) < 0) {
		ret = errno == ENOENT? -E_OSL_NOENT : -E_OSL_STAT;
		goto out;
	}
	*size = file_status.st_size;
	ret = -E_OSL_EMPTY;
	DEBUG_LOG("%s: size %zu\n", path, *size);
	if (!*size)
		goto out;
	*map = mmap(NULL, *size, mmap_prot, mmap_flags, fd, 0);
	if (*map == MAP_FAILED) {
		*map = NULL;
		ret = -E_OSL_MMAP;
		goto out;
	}
	ret = 1;
out:
	if (ret < 0 || !fd_ptr)
		close(fd);
	else
		*fd_ptr = fd;
	return ret;
}

/**
 * A wrapper for munmap(2).
 *
 * \param start The start address of the memory mapping.
 * \param length The size of the mapping.
 *
 * \return Positive on success, \p -E_MUNMAP on errors.
 *
 * \sa munmap(2), mmap_full_file().
 */
int osl_munmap(void *start, size_t length)
{
	int err;
	if (munmap(start, length) >= 0)
		return 1;
	err = errno;
	ERROR_LOG("munmap (%p/%zu) failed: %s\n", start, length,
		strerror(err));
	return -E_OSL_MUNMAP;
}

/**
 * Allocate a sufficiently large string and print into it.
 *
 * \param fmt A usual format string.
 *
 * Produce output according to \p fmt. No artificial bound on the length of the
 * resulting string is imposed.
 *
 * \return This function either returns a pointer to a string that must be
 * freed by the caller or \p NULL if memory allocation failed.
 *
 * \sa printf(3).
 */
__must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...)
{
	int n;
	size_t size = 100;
	char *p = malloc(size);

	if (!p)
		return NULL;
	while (1) {
		char *q;
		va_list ap;
		/* Try to print in the allocated space. */
		va_start(ap, fmt);
		n = vsnprintf(p, size, fmt, ap);
		va_end(ap);
		/* If that worked, return the string. */
		if (n > -1 && (unsigned)n < size)
			break;
		/* Else try again with more space. */
		if (n > -1) /* glibc 2.1 */
			size = n + 1; /* precisely what is needed */
		else /* glibc 2.0 */
			size *= 2; /* twice the old size */
		q = realloc(p, size);
		if (!q) {
			free(p);
			return NULL;
		}
		p = q;
	}
	return p;
}

/**
 * A wrapper for truncate(2)
 *
 * \param path Name of the regular file to truncate
 * \param size Number of bytes to \b shave \b off
 *
 * Truncate the regular file named by \a path by \a size bytes.
 *
 * \return Standard.
 *
 * \sa truncate(2)
 */
int truncate_file(const char *path, off_t size)
{
	int ret;
	struct stat statbuf;

	ret = osl_stat(path, &statbuf);
	if (ret < 0)
		return ret;
	ret = -E_OSL_BAD_SIZE;
	if (statbuf.st_size < size)
		return ret;
	if (truncate(path, statbuf.st_size - size) < 0)
		return -E_OSL_TRUNCATE;
	return 1;
}