95976d6817fba4e6571f4a2ac3eab3673af18353
[osl.git] / fd.c
1 /*
2 * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file fd.c Helper functions for file descriptor handling. */
8
9 #include <sys/types.h>
10 #include <dirent.h>
11 #include <sys/mman.h>
12 #include <fcntl.h>
13 #include <sys/select.h>
14
15 #include "log.h"
16 #include "osl.h"
17 #include "error.h"
18
19 /**
20 * Write a buffer to a file descriptor, re-write on short writes.
21 *
22 * \param fd The file descriptor.
23 * \param buf The buffer to be sent.
24 * \param len The length of \a buf.
25 *
26 * \return Standard. In any case, the number of bytes that have been written is
27 * stored in \a len.
28 */
29 int write_all(int fd, const char *buf, size_t *len)
30 {
31 size_t total = *len;
32
33 assert(total);
34 *len = 0;
35 while (*len < total) {
36 int ret = write(fd, buf + *len, total - *len);
37 if (ret == -1)
38 return -ERRNO_TO_ERROR(errno);
39 *len += ret;
40 }
41 return 1;
42 }
43
44 /**
45 * Wrapper for the open(2) system call.
46 *
47 * \param path The filename.
48 * \param flags The usual open(2) flags.
49 * \param mode Specifies the permissions to use.
50 *
51 * The mode parameter must be specified when O_CREAT is in the flags, and is
52 * ignored otherwise.
53 *
54 * \return The file descriptor on success, negative on errors.
55 *
56 * \sa open(2).
57 */
58 int osl_open(const char *path, int flags, mode_t mode)
59 {
60 int ret = open(path, flags, mode);
61
62 if (ret >= 0)
63 return ret;
64 return -ERRNO_TO_ERROR(errno);
65 }
66
67 /**
68 * Wrapper for chdir(2).
69 *
70 * \param path The specified directory.
71 *
72 * \return Standard.
73 */
74 static int para_chdir(const char *path)
75 {
76 int ret = chdir(path);
77
78 if (ret >= 0)
79 return 1;
80 return -ERRNO_TO_ERROR(errno);
81 }
82
83 /**
84 * Save the cwd and open a given directory.
85 *
86 * \param dirname Path to the directory to open.
87 * \param dir Result pointer.
88 * \param cwd File descriptor of the current working directory.
89 *
90 * \return Standard.
91 *
92 * Opening the current directory (".") and calling fchdir() to return is
93 * usually faster and more reliable than saving cwd in some buffer and calling
94 * chdir() afterwards.
95 *
96 * If \a cwd is not \p NULL "." is opened and the resulting file descriptor is
97 * stored in \a cwd. If the function returns success, and \a cwd is not \p
98 * NULL, the caller must close this file descriptor (probably after calling
99 * fchdir(*cwd)).
100 *
101 * On errors, the function undos everything, so the caller needs neither close
102 * any files, nor change back to the original working directory.
103 *
104 * \sa getcwd(3).
105 *
106 */
107 int para_opendir(const char *dirname, DIR **dir, int *cwd)
108 {
109 int ret;
110
111 if (cwd) {
112 ret = osl_open(".", O_RDONLY, 0);
113 if (ret < 0)
114 return ret;
115 *cwd = ret;
116 }
117 ret = para_chdir(dirname);
118 if (ret < 0)
119 goto close_cwd;
120 *dir = opendir(".");
121 if (*dir)
122 return 1;
123 ret = -ERRNO_TO_ERROR(errno);
124 /* Ignore return value of fchdir() and close(). We're busted anyway. */
125 if (cwd)
126 fchdir(*cwd);
127 close_cwd:
128 if (cwd)
129 close(*cwd);
130 return ret;
131 }
132
133 /**
134 * A wrapper for fchdir().
135 *
136 * \param fd An open file descriptor.
137 *
138 * \return Standard.
139 */
140 int para_fchdir(int fd)
141 {
142 if (fchdir(fd) < 0)
143 return -ERRNO_TO_ERROR(errno);
144 return 1;
145 }
146
147 /**
148 * Open a file and map it into memory.
149 *
150 * \param path Name of the regular file to map.
151 * \param open_mode Either \p O_RDONLY or \p O_RDWR.
152 * \param map On success, the mapping is returned here.
153 * \param size size of the mapping.
154 * \param fd_ptr The file descriptor of the mapping.
155 *
156 * If \a fd_ptr is \p NULL, the file descriptor resulting from the underlying
157 * open call is closed after mmap(). Otherwise the file is kept open and the
158 * file descriptor is returned in \a fd_ptr.
159 *
160 * \return Standard.
161 *
162 * \sa osl_open(), mmap(2).
163 */
164 int mmap_full_file(const char *path, int open_mode, void **map,
165 size_t *size, int *fd_ptr)
166 {
167 int fd, ret, mmap_prot, mmap_flags;
168 struct stat file_status;
169
170 if (open_mode == O_RDONLY) {
171 mmap_prot = PROT_READ;
172 mmap_flags = MAP_PRIVATE;
173 } else {
174 mmap_prot = PROT_READ | PROT_WRITE;
175 mmap_flags = MAP_SHARED;
176 }
177 ret = osl_open(path, open_mode, 0);
178 if (ret < 0)
179 return ret;
180 fd = ret;
181 if (fstat(fd, &file_status) < 0) {
182 ret = -ERRNO_TO_ERROR(errno);
183 goto out;
184 }
185 *size = file_status.st_size;
186 ret = -E_OSL_EMPTY;
187 DEBUG_LOG("%s: size %zu\n", path, *size);
188 if (!*size)
189 goto out;
190 *map = mmap(NULL, *size, mmap_prot, mmap_flags, fd, 0);
191 if (*map == MAP_FAILED) {
192 *map = NULL;
193 ret = -E_OSL_MMAP;
194 goto out;
195 }
196 ret = 1;
197 out:
198 if (ret < 0 || !fd_ptr)
199 close(fd);
200 else
201 *fd_ptr = fd;
202 return ret;
203 }
204
205 /**
206 * A wrapper for munmap(2).
207 *
208 * \param start The start address of the memory mapping.
209 * \param length The size of the mapping.
210 *
211 * \return Positive on success, \p -E_MUNMAP on errors.
212 *
213 * \sa munmap(2), mmap_full_file().
214 */
215 int para_munmap(void *start, size_t length)
216 {
217 int err;
218 if (munmap(start, length) >= 0)
219 return 1;
220 err = errno;
221 ERROR_LOG("munmap (%p/%zu) failed: %s\n", start, length,
222 strerror(err));
223 return -ERRNO_TO_ERROR(err);
224 }