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