1 /* $Id$ */
2 
3 /*
4  * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/mman.h>
21 
22 #include <fcntl.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "fdm.h"
27 
28 /*
29  * This implements shared memory using mmap'd files in TMPDIR.
30  */
31 
32 int	shm_expand(struct shm *, size_t);
33 
34 char	shm_block[BUFSIZ];
35 
36 #ifdef MAP_NOSYNC
37 #define SHM_FLAGS MAP_SHARED|MAP_NOSYNC
38 #else
39 #define SHM_FLAGS MAP_SHARED
40 #endif
41 #define SHM_PROT PROT_READ|PROT_WRITE
42 
43 /* Work out shm path. */
44 char *
shm_path(struct shm * shm)45 shm_path(struct shm *shm)
46 {
47 	static char	path[MAXPATHLEN];
48 
49 	if (ppath(path, sizeof path, "%s/%s", conf.tmp_dir, shm->name) != 0)
50 		return (NULL);
51 	return (path);
52 }
53 
54 /* Expand or reduce shm file to size. */
55 int
shm_expand(struct shm * shm,size_t size)56 shm_expand(struct shm *shm, size_t size)
57 {
58 	ssize_t	n;
59 
60 	if (size == shm->size)
61 		return (0);
62 
63 	if (size < shm->size)
64 		return (ftruncate(shm->fd, size) != 0);
65 
66 	if (lseek(shm->fd, shm->size, SEEK_SET) == -1)
67 		return (-1);
68 
69 	/*
70 	 * Fill the file using write(2) to avoid fragmentation problems on
71 	 * FreeBSD and also to detect disk full.
72 	 */
73 	while (size > sizeof shm_block) {
74 		if ((n = write(shm->fd, shm_block, sizeof shm_block)) == -1)
75 			return (-1);
76 		if (n != sizeof shm_block) {
77 			errno = EIO;
78 			return (-1);
79 		}
80 		size -= sizeof shm_block;
81 	}
82 	if (size > 0) {
83 		if ((n = write(shm->fd, shm_block, size)) == -1)
84 			return (-1);
85 		if ((size_t) n != size) {
86 			errno = EIO;
87 			return (-1);
88 		}
89 	}
90 
91 	/*
92 	 * Sync the fd, should hopefully fail if disk full.
93 	 */
94 	if (fsync(shm->fd) != 0)
95 		return (-1);
96 
97 	return (0);
98 }
99 
100 /* Create an shm file and map it. */
101 void *
shm_create(struct shm * shm,size_t size)102 shm_create(struct shm *shm, size_t size)
103 {
104 	int	 saved_errno;
105 	char	*path;
106 
107 	if (size == 0)
108 		fatalx("zero size");
109 
110 	if (ppath(
111 	    shm->name, sizeof shm->name, "%s.XXXXXXXXXX", __progname) != 0)
112 		return (NULL);
113 	if ((path = shm_path(shm)) == NULL)
114 		return (NULL);
115 	if ((shm->fd = mkstemp(path)) == -1)
116 		return (NULL);
117 	strlcpy(shm->name, xbasename(path), sizeof shm->name);
118 
119 	if (shm_expand(shm, size) != 0)
120 		goto error;
121 
122 	shm->data = mmap(NULL, size, SHM_PROT, SHM_FLAGS, shm->fd, 0);
123 	if (shm->data == MAP_FAILED)
124 		goto error;
125 	madvise(shm->data, size, MADV_SEQUENTIAL);
126 
127 	shm->size = size;
128 	return (shm->data);
129 
130 error:
131 	saved_errno = errno;
132 	unlink(path);
133 	errno = saved_errno;
134 	return (NULL);
135 }
136 
137 /* Destroy shm file. */
138 void
shm_destroy(struct shm * shm)139 shm_destroy(struct shm *shm)
140 {
141 	char	*path;
142 
143 	if (*shm->name == '\0')
144 		return;
145 
146 	shm_close(shm);
147 
148 	if ((path = shm_path(shm)) == NULL)
149 		fatal("unlink failed");
150 	if (unlink(path) != 0)
151 		fatal("unlink failed");
152 
153 	*shm->name = '\0';
154 }
155 
156 /* Close and unmap shm without destroying file. */
157 void
shm_close(struct shm * shm)158 shm_close(struct shm *shm)
159 {
160 	if (shm->fd == -1)
161 		return;
162 
163 	if (munmap(shm->data, shm->size) != 0)
164 		fatal("munmap failed");
165 	shm->data = NULL;
166 
167 	close(shm->fd);
168 	shm->fd = -1;
169 }
170 
171 /* Reopen and map shm file. */
172 void *
shm_reopen(struct shm * shm)173 shm_reopen(struct shm *shm)
174 {
175 	char	*path;
176 
177 	if ((path = shm_path(shm)) == NULL)
178 		return (NULL);
179 	if ((shm->fd = open(path, O_RDWR, 0)) == -1)
180 		return (NULL);
181 
182 	shm->data = mmap(NULL, shm->size, SHM_PROT, SHM_FLAGS, shm->fd, 0);
183 	if (shm->data == MAP_FAILED)
184 		return (NULL);
185 	madvise(shm->data, shm->size, MADV_SEQUENTIAL);
186 
187 	return (shm->data);
188 }
189 
190 /* Set ownership of shm file. */
191 int
shm_owner(struct shm * shm,uid_t uid,gid_t gid)192 shm_owner(struct shm *shm, uid_t uid, gid_t gid)
193 {
194 	if (fchown(shm->fd, uid, gid) != 0)
195 		return (-1);
196 
197 	return (0);
198 }
199 
200 /* Resize an shm file. */
201 void *
shm_resize(struct shm * shm,size_t nmemb,size_t size)202 shm_resize(struct shm *shm, size_t nmemb, size_t size)
203 {
204 	size_t	 newsize = nmemb * size;
205 
206 	if (size == 0)
207 		fatalx("zero size");
208 	if (SIZE_MAX / nmemb < size)
209 		fatalx("nmemb * size > SIZE_MAX");
210 
211 #ifndef HAVE_MREMAP
212 	if (munmap(shm->data, shm->size) != 0)
213 		fatal("munmap failed");
214 	shm->data = NULL;
215 #endif
216 
217 	if (shm_expand(shm, newsize) != 0)
218 		return (NULL);
219 
220 #ifdef HAVE_MREMAP
221 	shm->data = mremap(shm->data, shm->size, newsize, MREMAP_MAYMOVE);
222 #else
223 	shm->data = mmap(NULL, newsize, SHM_PROT, SHM_FLAGS, shm->fd, 0);
224 #endif
225 	if (shm->data == MAP_FAILED)
226 		return (NULL);
227 	madvise(shm->data, newsize, MADV_SEQUENTIAL);
228 
229 	shm->size = newsize;
230 	return (shm->data);
231 }
232