xref: /minix/minix/servers/ipc/shm.c (revision 7f5f010b)
1 #include "inc.h"
2 
3 #define MAX_SHM_NR 1024
4 
5 struct shm_struct {
6 	key_t key;
7 	int id;
8 	struct shmid_ds shmid_ds;
9 	vir_bytes page;
10 	int vm_id;
11 };
12 static struct shm_struct shm_list[MAX_SHM_NR];
13 static int shm_list_nr = 0;
14 
15 static struct shm_struct *shm_find_key(key_t key)
16 {
17 	int i;
18 	if (key == IPC_PRIVATE)
19 		return NULL;
20 	for (i = 0; i < shm_list_nr; i++)
21 		if (shm_list[i].key == key)
22 			return shm_list+i;
23 	return NULL;
24 }
25 
26 static struct shm_struct *shm_find_id(int id)
27 {
28 	int i;
29 	for (i = 0; i < shm_list_nr; i++)
30 		if (shm_list[i].id == id)
31 			return shm_list+i;
32 	return NULL;
33 }
34 
35 /*===========================================================================*
36  *				do_shmget		     		     *
37  *===========================================================================*/
38 int do_shmget(message *m)
39 {
40 	struct shm_struct *shm;
41 	long key, size, old_size;
42 	int flag;
43 	int id;
44 
45 	key = m->m_lc_ipc_shmget.key;
46 	old_size = size = m->m_lc_ipc_shmget.size;
47 	flag = m->m_lc_ipc_shmget.flag;
48 
49 	if ((shm = shm_find_key(key))) {
50 		if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
51 			return EACCES;
52 		if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
53 			return EEXIST;
54 		if (size && shm->shmid_ds.shm_segsz < size)
55 			return EINVAL;
56 		id = shm->id;
57 	} else { /* no key found */
58 		if (!(flag & IPC_CREAT))
59 			return ENOENT;
60 		if (size <= 0)
61 			return EINVAL;
62 		/* round up to a multiple of PAGE_SIZE */
63 		if (size % PAGE_SIZE)
64 			size += PAGE_SIZE - size % PAGE_SIZE;
65 		if (size <= 0)
66 			return EINVAL;
67 
68 		if (shm_list_nr == MAX_SHM_NR)
69 			return ENOMEM;
70 		/* TODO: shmmni should be changed... */
71 		if (identifier == SHMMNI)
72 			return ENOSPC;
73 		shm = &shm_list[shm_list_nr];
74 		memset(shm, 0, sizeof(struct shm_struct));
75 		shm->page = (vir_bytes) mmap(0, size,
76 					PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
77 		if (shm->page == (vir_bytes) MAP_FAILED)
78 			return ENOMEM;
79 		shm->vm_id = vm_getphys(sef_self(), (void *) shm->page);
80 		memset((void *)shm->page, 0, size);
81 
82 		shm->shmid_ds.shm_perm.cuid =
83 			shm->shmid_ds.shm_perm.uid = getnuid(who_e);
84 		shm->shmid_ds.shm_perm.cgid =
85 			shm->shmid_ds.shm_perm.gid = getngid(who_e);
86 		shm->shmid_ds.shm_perm.mode = flag & 0777;
87 		shm->shmid_ds.shm_segsz = old_size;
88 		shm->shmid_ds.shm_atime = 0;
89 		shm->shmid_ds.shm_dtime = 0;
90 		shm->shmid_ds.shm_ctime = time(NULL);
91 		shm->shmid_ds.shm_cpid = getnpid(who_e);
92 		shm->shmid_ds.shm_lpid = 0;
93 		shm->shmid_ds.shm_nattch = 0;
94 		shm->id = id = identifier++;
95 		shm->key = key;
96 
97 		shm_list_nr++;
98 	}
99 
100 	m->m_lc_ipc_shmget.retid = id;
101 	return OK;
102 }
103 
104 /*===========================================================================*
105  *				do_shmat		     		     *
106  *===========================================================================*/
107 int do_shmat(message *m)
108 {
109 	int id, flag;
110 	vir_bytes addr;
111 	void *ret;
112 	struct shm_struct *shm;
113 
114 	id = m->m_lc_ipc_shmat.id;
115 	addr = (vir_bytes) m->m_lc_ipc_shmat.addr;
116 	flag = m->m_lc_ipc_shmat.flag;
117 
118 	if (addr && (addr % PAGE_SIZE)) {
119 		if (flag & SHM_RND)
120 			addr -= (addr % PAGE_SIZE);
121 		else
122 			return EINVAL;
123 	}
124 
125 	if (!(shm = shm_find_id(id)))
126 		return EINVAL;
127 
128 	if (flag & SHM_RDONLY)
129 		flag = 0444;
130 	else
131 		flag = 0666;
132 	if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
133 		return EACCES;
134 
135 	ret = vm_remap(who_e, sef_self(), (void *)addr, (void *)shm->page,
136 			shm->shmid_ds.shm_segsz);
137 	if (ret == MAP_FAILED)
138 		return ENOMEM;
139 
140 	shm->shmid_ds.shm_atime = time(NULL);
141 	shm->shmid_ds.shm_lpid = getnpid(who_e);
142 	/* nattach is updated lazily */
143 
144 	m->m_lc_ipc_shmat.retaddr = ret;
145 	return OK;
146 }
147 
148 /*===========================================================================*
149  *				update_refcount_and_destroy		     *
150  *===========================================================================*/
151 void update_refcount_and_destroy(void)
152 {
153 	int i, j;
154 
155 	for (i = 0, j = 0; i < shm_list_nr; i++) {
156 		u8_t rc;
157 
158 		rc = vm_getrefcount(sef_self(), (void *) shm_list[i].page);
159 		if (rc == (u8_t) -1) {
160 			printf("IPC: can't find physical region.\n");
161 			continue;
162 		}
163 		shm_list[i].shmid_ds.shm_nattch = rc - 1;
164 
165 		if (shm_list[i].shmid_ds.shm_nattch ||
166 			!(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
167 			if (i != j)
168 				shm_list[j] = shm_list[i];
169 			j++;
170 		} else {
171 			int size = shm_list[i].shmid_ds.shm_segsz;
172 			if (size % PAGE_SIZE)
173 				size += PAGE_SIZE - size % PAGE_SIZE;
174 			munmap((void *)shm_list[i].page, size);
175 		}
176 	}
177 	shm_list_nr = j;
178 }
179 
180 /*===========================================================================*
181  *				do_shmdt		     		     *
182  *===========================================================================*/
183 int do_shmdt(message *m)
184 {
185 	vir_bytes addr;
186 	phys_bytes vm_id;
187 	int i;
188 
189 	addr = (vir_bytes) m->m_lc_ipc_shmdt.addr;
190 
191 	if ((vm_id = vm_getphys(who_e, (void *) addr)) == 0)
192 		return EINVAL;
193 
194 	for (i = 0; i < shm_list_nr; i++) {
195 		if (shm_list[i].vm_id == vm_id) {
196 			struct shm_struct *shm = &shm_list[i];
197 
198 			shm->shmid_ds.shm_atime = time(NULL);
199 			shm->shmid_ds.shm_lpid = getnpid(who_e);
200 			/* nattch is updated lazily */
201 
202 			vm_unmap(who_e, (void *) addr);
203 			break;
204 		}
205 	}
206 	if (i == shm_list_nr)
207 		printf("IPC: do_shmdt impossible error! could not find id %lu to unmap\n",
208 			vm_id);
209 
210 	update_refcount_and_destroy();
211 
212 	return OK;
213 }
214 
215 /*===========================================================================*
216  *				do_shmctl		     		     *
217  *===========================================================================*/
218 int do_shmctl(message *m)
219 {
220 	int id = m->m_lc_ipc_shmctl.id;
221 	int cmd = m->m_lc_ipc_shmctl.cmd;
222 	struct shmid_ds *ds = (struct shmid_ds *)m->m_lc_ipc_shmctl.buf;
223 	struct shmid_ds tmp_ds;
224 	struct shm_struct *shm = NULL;
225 	struct shminfo sinfo;
226 	struct shm_info s_info;
227 	uid_t uid;
228 	int r, i;
229 
230 	if (cmd == IPC_STAT)
231 		update_refcount_and_destroy();
232 
233 	if ((cmd == IPC_STAT ||
234 		cmd == IPC_SET ||
235 		cmd == IPC_RMID) &&
236 		!(shm = shm_find_id(id)))
237 		return EINVAL;
238 
239 	switch (cmd) {
240 	case IPC_STAT:
241 		if (!ds)
242 			return EFAULT;
243 		/* check whether it has read permission */
244 		if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444))
245 			return EACCES;
246 		r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
247 			who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
248 		if (r != OK)
249 			return EFAULT;
250 		break;
251 	case IPC_SET:
252 		uid = getnuid(who_e);
253 		if (uid != shm->shmid_ds.shm_perm.cuid &&
254 			uid != shm->shmid_ds.shm_perm.uid &&
255 			uid != 0)
256 			return EPERM;
257 		r = sys_datacopy(who_e, (vir_bytes)ds,
258 			SELF, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds));
259 		if (r != OK)
260 			return EFAULT;
261 		shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
262 		shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
263 		shm->shmid_ds.shm_perm.mode &= ~0777;
264 		shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666;
265 		shm->shmid_ds.shm_ctime = time(NULL);
266 		break;
267 	case IPC_RMID:
268 		uid = getnuid(who_e);
269 		if (uid != shm->shmid_ds.shm_perm.cuid &&
270 			uid != shm->shmid_ds.shm_perm.uid &&
271 			uid != 0)
272 			return EPERM;
273 		shm->shmid_ds.shm_perm.mode |= SHM_DEST;
274 		/* destroy if possible */
275 		update_refcount_and_destroy();
276 		break;
277 	case IPC_INFO:
278 		if (!ds)
279 			return EFAULT;
280 		sinfo.shmmax = (unsigned long) -1;
281 		sinfo.shmmin = 1;
282 		sinfo.shmmni = MAX_SHM_NR;
283 		sinfo.shmseg = (unsigned long) -1;
284 		sinfo.shmall = (unsigned long) -1;
285 		r = sys_datacopy(SELF, (vir_bytes)&sinfo,
286 			who_e, (vir_bytes)ds, sizeof(struct shminfo));
287 		if (r != OK)
288 			return EFAULT;
289 		m->m_lc_ipc_shmctl.ret = (shm_list_nr - 1);
290 		if (m->m_lc_ipc_shmctl.ret < 0)
291 			m->m_lc_ipc_shmctl.ret = 0;
292 		break;
293 	case SHM_INFO:
294 		if (!ds)
295 			return EFAULT;
296 		s_info.used_ids = shm_list_nr;
297 		s_info.shm_tot = 0;
298 		for (i = 0; i < shm_list_nr; i++)
299 			s_info.shm_tot +=
300 				shm_list[i].shmid_ds.shm_segsz/PAGE_SIZE;
301 		s_info.shm_rss = s_info.shm_tot;
302 		s_info.shm_swp = 0;
303 		s_info.swap_attempts = 0;
304 		s_info.swap_successes = 0;
305 		r = sys_datacopy(SELF, (vir_bytes)&s_info,
306 			who_e, (vir_bytes)ds, sizeof(struct shm_info));
307 		if (r != OK)
308 			return EFAULT;
309 		m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
310 		if (m->m_lc_ipc_shmctl.ret < 0)
311 			m->m_lc_ipc_shmctl.ret = 0;
312 		break;
313 	case SHM_STAT:
314 		if (id < 0 || id >= shm_list_nr)
315 			return EINVAL;
316 		shm = &shm_list[id];
317 		r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
318 			who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
319 		if (r != OK)
320 			return EFAULT;
321 		m->m_lc_ipc_shmctl.ret = shm->id;
322 		break;
323 	default:
324 		return EINVAL;
325 	}
326 	return OK;
327 }
328 
329 #if 0
330 static void list_shm_ds(void)
331 {
332 	int i;
333 	printf("key\tid\tpage\n");
334 	for (i = 0; i < shm_list_nr; i++)
335 		printf("%ld\t%d\t%lx\n",
336 			shm_list[i].key,
337 			shm_list[i].id,
338 			shm_list[i].page);
339 }
340 #endif
341 
342 /*===========================================================================*
343  *				is_shm_nil		     		     *
344  *===========================================================================*/
345 int is_shm_nil(void)
346 {
347 	return (shm_list_nr == 0);
348 }
349