vm: change NO_MEM to a more impossible value
[minix.git] / servers / ipc / shm.c
bloba949f4d25ca860716d46ea92c4ef8fe85073cec8
1 #include "inc.h"
3 #define MAX_SHM_NR 1024
5 struct shm_struct {
6 key_t key;
7 int id;
8 struct shmid_ds shmid_ds;
9 vir_bytes page;
10 phys_bytes phys;
12 static struct shm_struct shm_list[MAX_SHM_NR];
13 static int shm_list_nr = 0;
15 static struct shm_struct *shm_find_key(key_t key)
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;
26 static struct shm_struct *shm_find_id(int id)
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;
35 /*===========================================================================*
36 * do_shmget *
37 *===========================================================================*/
38 int do_shmget(message *m)
40 struct shm_struct *shm;
41 long key, size, old_size;
42 int flag;
43 int id;
45 key = m->SHMGET_KEY;
46 old_size = size = m->SHMGET_SIZE;
47 flag = m->SHMGET_FLAG;
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 % I386_PAGE_SIZE)
64 size += I386_PAGE_SIZE - size % I386_PAGE_SIZE;
65 if (size <= 0)
66 return EINVAL;
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) minix_mmap(0, size,
76 PROT_READ|PROT_WRITE,
77 MAP_CONTIG|MAP_PREALLOC|MAP_ANON|MAP_IPC_SHARED,
78 -1, 0);
79 if (shm->page == (vir_bytes) MAP_FAILED)
80 return ENOMEM;
81 shm->phys = vm_getphys(SELF_E, (void *) shm->page);
82 memset((void *)shm->page, 0, size);
84 shm->shmid_ds.shm_perm.cuid =
85 shm->shmid_ds.shm_perm.uid = getnuid(who_e);
86 shm->shmid_ds.shm_perm.cgid =
87 shm->shmid_ds.shm_perm.gid = getngid(who_e);
88 shm->shmid_ds.shm_perm.mode = flag & 0777;
89 shm->shmid_ds.shm_segsz = old_size;
90 shm->shmid_ds.shm_atime = 0;
91 shm->shmid_ds.shm_dtime = 0;
92 shm->shmid_ds.shm_ctime = time(NULL);
93 shm->shmid_ds.shm_cpid = getnpid(who_e);
94 shm->shmid_ds.shm_lpid = 0;
95 shm->shmid_ds.shm_nattch = 0;
96 shm->id = id = identifier++;
97 shm->key = key;
99 shm_list_nr++;
102 m->SHMGET_RETID = id;
103 return OK;
106 /*===========================================================================*
107 * do_shmat *
108 *===========================================================================*/
109 int do_shmat(message *m)
111 int id, flag;
112 vir_bytes addr;
113 void *ret;
114 struct shm_struct *shm;
116 id = m->SHMAT_ID;
117 addr = (vir_bytes) m->SHMAT_ADDR;
118 flag = m->SHMAT_FLAG;
120 if (addr && (addr % I386_PAGE_SIZE)) {
121 if (flag & SHM_RND)
122 addr -= (addr % I386_PAGE_SIZE);
123 else
124 return EINVAL;
127 if (!(shm = shm_find_id(id)))
128 return EINVAL;
130 if (flag & SHM_RDONLY)
131 flag = 0444;
132 else
133 flag = 0666;
134 if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
135 return EACCES;
137 ret = vm_remap(who_e, SELF_E, (void *)addr, (void *)shm->page,
138 shm->shmid_ds.shm_segsz);
139 if (ret == MAP_FAILED)
140 return ENOMEM;
142 shm->shmid_ds.shm_atime = time(NULL);
143 shm->shmid_ds.shm_lpid = getnpid(who_e);
144 /* nattach is updated lazily */
146 m->SHMAT_RETADDR = (long) ret;
147 return OK;
150 /*===========================================================================*
151 * update_refcount_and_destroy *
152 *===========================================================================*/
153 void update_refcount_and_destroy(void)
155 int i, j;
157 for (i = 0, j = 0; i < shm_list_nr; i++) {
158 u8_t rc;
160 rc = vm_getrefcount(SELF_E, (void *) shm_list[i].page);
161 if (rc == (u8_t) -1) {
162 printf("IPC: can't find physical region.\n");
163 continue;
165 shm_list[i].shmid_ds.shm_nattch = rc - 1;
167 if (shm_list[i].shmid_ds.shm_nattch ||
168 !(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
169 if (i != j)
170 shm_list[j] = shm_list[i];
171 j++;
172 } else {
173 int size = shm_list[i].shmid_ds.shm_segsz;
174 if (size % I386_PAGE_SIZE)
175 size += I386_PAGE_SIZE - size % I386_PAGE_SIZE;
176 minix_munmap((void *)shm_list[i].page, size);
179 shm_list_nr = j;
182 /*===========================================================================*
183 * do_shmdt *
184 *===========================================================================*/
185 int do_shmdt(message *m)
187 vir_bytes addr;
188 phys_bytes paddr;
189 int i;
191 addr = m->SHMDT_ADDR;
193 if ((paddr = vm_getphys(who_e, (void *) addr)) == 0)
194 return EINVAL;
196 for (i = 0; i < shm_list_nr; i++) {
197 if (shm_list[i].phys == paddr) {
198 struct shm_struct *shm = &shm_list[i];
200 shm->shmid_ds.shm_atime = time(NULL);
201 shm->shmid_ds.shm_lpid = getnpid(who_e);
202 /* nattch is updated lazily */
204 vm_unmap(who_e, (void *) addr);
205 break;
208 if (i == shm_list_nr)
209 fprintf(stderr, "IPC: do_shmdt impossible error!\n");
211 update_refcount_and_destroy();
213 return OK;
216 /*===========================================================================*
217 * do_shmctl *
218 *===========================================================================*/
219 int do_shmctl(message *m)
221 int id = m->SHMCTL_ID;
222 int cmd = m->SHMCTL_CMD;
223 struct shmid_ds *ds = (struct shmid_ds *)m->SHMCTL_BUF;
224 struct shmid_ds tmp_ds;
225 struct shm_struct *shm = NULL;
226 struct shminfo sinfo;
227 struct shm_info s_info;
228 uid_t uid;
229 int r, i;
231 if (cmd == IPC_STAT)
232 update_refcount_and_destroy();
234 if ((cmd == IPC_STAT ||
235 cmd == IPC_SET ||
236 cmd == IPC_RMID) &&
237 !(shm = shm_find_id(id)))
238 return EINVAL;
240 switch (cmd) {
241 case IPC_STAT:
242 if (!ds)
243 return EFAULT;
244 /* check whether it has read permission */
245 if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444))
246 return EACCES;
247 r = sys_datacopy(SELF_E, (vir_bytes)&shm->shmid_ds,
248 who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
249 if (r != OK)
250 return EFAULT;
251 break;
252 case IPC_SET:
253 uid = getnuid(who_e);
254 if (uid != shm->shmid_ds.shm_perm.cuid &&
255 uid != shm->shmid_ds.shm_perm.uid &&
256 uid != 0)
257 return EPERM;
258 r = sys_datacopy(who_e, (vir_bytes)ds,
259 SELF_E, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds));
260 if (r != OK)
261 return EFAULT;
262 shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
263 shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
264 shm->shmid_ds.shm_perm.mode &= ~0777;
265 shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666;
266 shm->shmid_ds.shm_ctime = time(NULL);
267 break;
268 case IPC_RMID:
269 uid = getnuid(who_e);
270 if (uid != shm->shmid_ds.shm_perm.cuid &&
271 uid != shm->shmid_ds.shm_perm.uid &&
272 uid != 0)
273 return EPERM;
274 shm->shmid_ds.shm_perm.mode |= SHM_DEST;
275 /* destroy if possible */
276 update_refcount_and_destroy();
277 break;
278 case IPC_INFO:
279 if (!ds)
280 return EFAULT;
281 sinfo.shmmax = (unsigned long) -1;
282 sinfo.shmmin = 1;
283 sinfo.shmmni = MAX_SHM_NR;
284 sinfo.shmseg = (unsigned long) -1;
285 sinfo.shmall = (unsigned long) -1;
286 r = sys_datacopy(SELF_E, (vir_bytes)&sinfo,
287 who_e, (vir_bytes)ds, sizeof(struct shminfo));
288 if (r != OK)
289 return EFAULT;
290 m->SHMCTL_RET = shm_list_nr - 1;
291 if (m->SHMCTL_RET < 0)
292 m->SHMCTL_RET = 0;
293 break;
294 case SHM_INFO:
295 if (!ds)
296 return EFAULT;
297 s_info.used_ids = shm_list_nr;
298 s_info.shm_tot = 0;
299 for (i = 0; i < shm_list_nr; i++)
300 s_info.shm_tot +=
301 shm_list[i].shmid_ds.shm_segsz/I386_PAGE_SIZE;
302 s_info.shm_rss = s_info.shm_tot;
303 s_info.shm_swp = 0;
304 s_info.swap_attempts = 0;
305 s_info.swap_successes = 0;
306 r = sys_datacopy(SELF_E, (vir_bytes)&s_info,
307 who_e, (vir_bytes)ds, sizeof(struct shm_info));
308 if (r != OK)
309 return EFAULT;
310 m->SHMCTL_RET = shm_list_nr - 1;
311 if (m->SHMCTL_RET < 0)
312 m->SHMCTL_RET = 0;
313 break;
314 case SHM_STAT:
315 if (id < 0 || id >= shm_list_nr)
316 return EINVAL;
317 shm = &shm_list[id];
318 r = sys_datacopy(SELF_E, (vir_bytes)&shm->shmid_ds,
319 who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
320 if (r != OK)
321 return EFAULT;
322 m->SHMCTL_RET = shm->id;
323 break;
324 default:
325 return EINVAL;
327 return OK;
330 #if 0
331 static void list_shm_ds(void)
333 int i;
334 printf("key\tid\tpage\n");
335 for (i = 0; i < shm_list_nr; i++)
336 printf("%ld\t%d\t%lx\n",
337 shm_list[i].key,
338 shm_list[i].id,
339 shm_list[i].page);
341 #endif
343 /*===========================================================================*
344 * is_shm_nil *
345 *===========================================================================*/
346 int is_shm_nil(void)
348 return (shm_list_nr == 0);