649c0d2ba2471c369a47370c45c57ef656363bcb
[monolithium.git] / kernel / src / object.c
1 /*
2  * object.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <object.h>
21 #include <process.h>
22 #include <thread.h>
23 #include <heap.h>
24 #include <pipe.h>
25
26 typedef void (*object_cleanup_proc_t)(object_t*);
27 typedef dword_t (*object_pre_wait_proc_t)(object_t*, void*, wait_condition_t*);
28 typedef void (*object_post_wait_proc_t)(object_t*, void*, wait_result_t);
29
30 extern void file_cleanup(object_t*);
31 extern void file_instance_cleanup(object_t*);
32 extern dword_t semaphore_pre_wait(object_t *obj, void *parameter, wait_condition_t *condition);
33 extern void semaphore_post_wait(object_t *obj, void *parameter, wait_result_t result);
34
35 static DECLARE_LOCK(obj_lock);
36 static DECLARE_LIST(anonymous_objects);
37 static list_entry_t named_objects[256];
38 static list_entry_t objects_by_type[OBJECT_TYPE_MAX];
39
40 static struct
41 {
42     object_cleanup_proc_t cleanup;
43     object_pre_wait_proc_t pre_wait;
44     object_post_wait_proc_t post_wait;
45 } type_info[OBJECT_TYPE_MAX] =
46 {
47     { .cleanup = file_cleanup,          .pre_wait = NULL,               .post_wait = NULL                },
48     { .cleanup = file_instance_cleanup, .pre_wait = NULL,               .post_wait = NULL                },
49     { .cleanup = NULL,                  .pre_wait = NULL,               .post_wait = NULL                },
50     { .cleanup = pipe_cleanup,          .pre_wait = pipe_pre_wait,      .post_wait = NULL                },
51     { .cleanup = process_cleanup,       .pre_wait = process_pre_wait,   .post_wait = NULL                },
52     { .cleanup = thread_cleanup,        .pre_wait = thread_pre_wait,    .post_wait = NULL                },
53     { .cleanup = memory_cleanup,        .pre_wait = NULL,               .post_wait = NULL                },
54     { .cleanup = NULL,                  .pre_wait = semaphore_pre_wait, .post_wait = semaphore_post_wait },
55     { .cleanup = NULL,                  .pre_wait = NULL,               .post_wait = NULL                },
56 };
57
58 static inline byte_t get_name_hash(const char *name)
59 {
60     byte_t sum = 0;
61     while (*name) sum += *name++;
62     return sum;
63 }
64
65 static handle_t insert_object(object_t *obj, access_flags_t access_flags)
66 {
67     handle_t i;
68     process_t *proc = get_previous_mode() == USER_MODE ? get_current_process() : kernel_process;
69
70     lock_acquire(&proc->handle_table_lock);
71
72     for (i = 0; i < proc->handle_table_size; i++) if (!proc->handle_table[i].obj)
73     {
74         proc->handle_table[i].obj = obj;
75         proc->handle_table[i].access_flags = access_flags;
76         proc->handle_count++;
77         goto cleanup;
78     }
79
80     handle_info_t *expanded_table = (handle_info_t*)heap_realloc(&evictable_heap, proc->handle_table, proc->handle_table_size * 2);
81     if (expanded_table == NULL)
82     {
83         i = INVALID_HANDLE;
84         goto cleanup;
85     }
86
87     expanded_table[proc->handle_table_size].obj = obj;
88     for (i = proc->handle_table_size + 1; i < proc->handle_table_size * 2; i++) expanded_table[i].obj = NULL;
89
90     proc->handle_table = expanded_table;
91     i = proc->handle_table_size;
92     proc->handle_table_size *= 2;
93
94 cleanup:
95     lock_release(&proc->handle_table_lock);
96     return i;
97 }
98
99 static bool_t access_check(object_t *obj, access_flags_t access)
100 {
101     if (check_privileges(PRIVILEGE_ACCESS_ALL)) return TRUE;
102
103     list_entry_t *ptr;
104     dword_t uid = get_current_uid();
105
106     for (ptr = obj->acl.next; ptr != &obj->acl; ptr = ptr->next)
107     {
108         access_control_entry_t *ace = CONTAINER_OF(ptr, access_control_entry_t, link);
109         if ((ace->uid == ALL_USERS || ace->uid == uid) && access == (access & ace->access_mask)) return TRUE;
110     }
111
112     return FALSE;
113 }
114
115 void reference(object_t *object)
116 {
117     lock_acquire(&obj_lock);
118     object->ref_count++;
119     lock_release(&obj_lock);
120 }
121
122 void dereference(object_t *object)
123 {
124     lock_acquire(&obj_lock);
125     dword_t ref_count = --object->ref_count;
126
127     if (!ref_count)
128     {
129         list_remove(&object->by_name_list);
130         list_remove(&object->by_type_list);
131     }
132
133     lock_release(&obj_lock);
134
135     if (!ref_count)
136     {
137         if (type_info[object->type].cleanup != NULL) type_info[object->type].cleanup(object);
138         free(object);
139     }
140 }
141
142 bool_t reference_by_handle(handle_t handle, object_type_t type, object_t **object)
143 {
144     bool_t result = FALSE;
145     process_t *proc = get_previous_mode() == USER_MODE ? get_current_process() : kernel_process;
146
147     if (handle >= proc->handle_table_size) return FALSE;
148     lock_acquire(&obj_lock);
149     lock_acquire_shared(&proc->handle_table_lock);
150
151     *object = proc->handle_table[handle].obj;
152     if (*object != NULL && (type == OBJECT_ANY_TYPE || (*object)->type == type))
153     {
154         (*object)->ref_count++;
155         result = TRUE;
156     }
157
158     lock_release(&proc->handle_table_lock);
159     lock_release(&obj_lock);
160     return result;
161 }
162
163 bool_t reference_by_name(const char *name, object_type_t type, object_t **object)
164 {
165     list_entry_t *ptr;
166     bool_t result = FALSE;
167     byte_t hash = get_name_hash(name);
168
169     lock_acquire(&obj_lock);
170
171     for (ptr = named_objects[hash].next; ptr != &named_objects[hash]; ptr = ptr->next)
172     {
173         object_t *obj = CONTAINER_OF(ptr, object_t, by_name_list);
174
175         if ((obj->name != NULL) && (strcmp(obj->name, name) == 0) && (obj->type == type))
176         {
177             obj->ref_count++;
178             *object = obj;
179             result = TRUE;
180             break;
181         }
182     }
183
184     lock_release(&obj_lock);
185     return result;
186 }
187
188 dword_t grant_access(object_t *obj, dword_t uid, access_flags_t access)
189 {
190     dword_t ret = ERR_SUCCESS;
191     reference(obj);
192     lock_acquire(&obj->acl_lock);
193
194     if (!access_check(obj, access))
195     {
196         ret = ERR_FORBIDDEN;
197         goto cleanup;
198     }
199
200     list_entry_t *ptr;
201     bool_t done = FALSE;
202
203     for (ptr = obj->acl.next; ptr != &obj->acl; ptr = ptr->next)
204     {
205         access_control_entry_t *ace = CONTAINER_OF(ptr, access_control_entry_t, link);
206         if (ace->uid == uid && (ace->access_mask & access) == access)
207         {
208             ace->access_mask |= access;
209             done = TRUE;
210             break;
211         }
212     }
213
214     if (!done)
215     {
216         access_control_entry_t *ace = malloc(sizeof(access_control_entry_t));
217         if (ace == NULL)
218         {
219             ret = ERR_NOMEMORY;
220             goto cleanup;
221         }
222
223         ace->uid = uid;
224         ace->access_mask = access;
225         list_append(&obj->acl, &ace->link);
226     }
227
228 cleanup:
229     lock_release(&obj->acl_lock);
230     dereference(obj);
231     return ret;
232 }
233
234 dword_t revoke_access(object_t *obj, dword_t uid, access_flags_t access)
235 {
236     dword_t ret = ERR_SUCCESS;
237     reference(obj);
238     lock_acquire(&obj->acl_lock);
239
240     if (get_current_uid() != obj->owner)
241     {
242         ret = ERR_FORBIDDEN;
243         goto cleanup;
244     }
245
246     list_entry_t *ptr;
247
248     for (ptr = obj->acl.next; ptr != &obj->acl; ptr = ptr->next)
249     {
250         access_control_entry_t *ace = CONTAINER_OF(ptr, access_control_entry_t, link);
251         if (ace->uid == uid && (ace->access_mask & access) == ace->access_mask)
252         {
253             if (!(ace->access_mask &= ~access))
254             {
255                 ptr = ptr->prev;
256                 list_remove(&ace->link);
257                 free(ace);
258                 ace = NULL;
259             }
260         }
261     }
262
263 cleanup:
264     lock_release(&obj->acl_lock);
265     dereference(obj);
266     return ret;
267 }
268
269 dword_t create_object(object_t *obj)
270 {
271     if (obj->name != NULL)
272     {
273         object_t *other_obj;
274
275         if (reference_by_name(obj->name, obj->type, &other_obj))
276         {
277             dereference(other_obj);
278             return ERR_EXISTS;
279         }
280     }
281
282     obj->ref_count = 1;
283     obj->open_count = 0;
284     obj->owner = get_current_uid();
285     list_init(&obj->acl);
286
287     access_control_entry_t *ace = malloc(sizeof(access_control_entry_t));
288     ace->uid = get_current_uid();
289     ace->access_mask = FULL_ACCESS;
290     list_append(&obj->acl, &ace->link);
291
292     lock_acquire(&obj_lock);
293     if (obj->name) list_append(&named_objects[get_name_hash(obj->name)], &obj->by_name_list);
294     else list_append(&anonymous_objects, &obj->by_name_list);
295     list_append(&objects_by_type[obj->type], &obj->by_type_list);
296     lock_release(&obj_lock);
297
298     return ERR_SUCCESS;
299 }
300
301 dword_t open_object(object_t *obj, access_flags_t access_flags, handle_t *handle)
302 {
303     dword_t ret = ERR_SUCCESS;
304     lock_acquire(&obj_lock);
305
306     if (!access_check(obj, access_flags))
307     {
308         ret = ERR_FORBIDDEN;
309         goto done;
310     }
311
312     handle_t new_handle = insert_object(obj, access_flags);
313     if (new_handle == INVALID_HANDLE)
314     {
315         ret = ERR_NOMEMORY;
316         goto done;
317     }
318
319     *handle = new_handle;
320     obj->ref_count++;
321     obj->open_count++;
322
323 done:
324     lock_release(&obj_lock);
325     return ret;
326 }
327
328 dword_t open_object_by_name(const char *name, object_type_t type, access_flags_t access_flags, handle_t *handle)
329 {
330     dword_t ret = ERR_SUCCESS;
331     object_t *obj;
332
333     if (!reference_by_name(name, type, &obj)) return ERR_NOTFOUND;
334     lock_acquire(&obj_lock);
335
336     if (!access_check(obj, access_flags))
337     {
338         ret = ERR_FORBIDDEN;
339         goto done;
340     }
341
342     handle_t new_handle = insert_object(obj, access_flags);
343     if (new_handle == INVALID_HANDLE)
344     {
345         ret = ERR_NOMEMORY;
346         goto done;
347     }
348
349     *handle = new_handle;
350     obj->open_count++;
351
352 done:
353     lock_release(&obj_lock);
354     if (ret != ERR_SUCCESS) dereference(obj);
355     return ret;
356 }
357
358 void close_object_internal(object_t *obj)
359 {
360     lock_acquire(&obj_lock);
361
362     obj->open_count--;
363     qword_t ref_count = --obj->ref_count;
364
365     if (!ref_count)
366     {
367         ASSERT(obj->open_count == 0);
368         list_remove(&obj->by_name_list);
369         list_remove(&obj->by_type_list);
370     }
371
372     lock_release(&obj_lock);
373
374     if (!ref_count)
375     {
376         if (type_info[obj->type].cleanup != NULL) type_info[obj->type].cleanup(obj);
377         free(obj);
378     }
379 }
380
381 sysret_t syscall_close_object(handle_t handle)
382 {
383     dword_t ret = ERR_SUCCESS;
384     process_t *proc = get_previous_mode() == USER_MODE ? get_current_process() : kernel_process;
385
386     reference(&proc->header);
387     lock_acquire(&proc->handle_table_lock);
388
389     if (handle >= proc->handle_table_size)
390     {
391         lock_release(&proc->handle_table_lock);
392         dereference(&proc->header);
393         return ERR_NOTFOUND;
394     }
395
396     object_t *obj = proc->handle_table[handle].obj;
397
398     if (obj != NULL)
399     {
400         close_object_internal(obj);
401
402         proc->handle_table[handle].obj = NULL;
403         proc->handle_count--;
404     }
405     else
406     {
407         ret = ERR_NOTFOUND;
408     }
409
410     lock_release(&proc->handle_table_lock);
411     dereference(&proc->header);
412
413     return ret;
414 }
415
416 sysret_t syscall_query_handle(handle_t handle, handle_info_type_t type, void *buffer, size_t size)
417 {
418     dword_t ret = ERR_SUCCESS;
419     process_t *proc;
420     void *safe_buffer = NULL;
421
422     if (get_previous_mode() == USER_MODE)
423     {
424         proc = get_current_process();
425         if (!check_usermode(buffer, size)) return ERR_BADPTR;
426         safe_buffer = malloc(size);
427         if (safe_buffer == NULL) return ERR_NOMEMORY;
428         memset(safe_buffer, 0, size);
429     }
430     else
431     {
432         proc = kernel_process;
433         safe_buffer = buffer;
434     }
435
436     reference(&proc->header);
437     lock_acquire(&proc->handle_table_lock);
438
439     object_t *obj = proc->handle_table[handle].obj;
440     if (obj == NULL)
441     {
442         ret = ERR_NOTFOUND;
443         goto cleanup;
444     }
445
446     char *name = obj->name ? obj->name : "<anonymous>";
447
448     switch (type)
449     {
450     case HANDLE_INFO_NAME:
451         strncpy(safe_buffer, name, size);
452         if (size < strlen(name) + 1) ret = ERR_SMALLBUF;
453         break;
454
455     case HANDLE_INFO_TYPE:
456         *((object_type_t*)safe_buffer) = obj->type;
457         if (size < sizeof(object_type_t)) ret = ERR_SMALLBUF;
458         break;
459
460     default:
461         ret = ERR_INVALID;
462     }
463
464     if (get_previous_mode() == USER_MODE)
465     {
466         EH_TRY memcpy(buffer, safe_buffer, size);
467         EH_CATCH ret = ERR_BADPTR;
468         EH_DONE;
469     }
470
471 cleanup:
472     lock_release(&proc->handle_table_lock);
473     dereference(&proc->header);
474     if (get_previous_mode() == USER_MODE) free(safe_buffer);
475     return ret;
476 }
477
478 sysret_t syscall_duplicate_handle(handle_t source_process, handle_t handle, handle_t dest_process, handle_t *duplicate)
479 {
480     process_t *proc;
481     handle_t safe_handle;
482
483     if (get_previous_mode() == USER_MODE && !check_usermode(duplicate, sizeof(handle_t)))
484     {
485         return ERR_BADPTR;
486     }
487
488     if (source_process != INVALID_HANDLE)
489     {
490         if (!reference_by_handle(source_process, OBJECT_PROCESS, (object_t**)&proc)) return ERR_INVALID;
491     }
492     else
493     {
494         proc = get_previous_mode() == USER_MODE ? get_current_process() : kernel_process;
495         reference(&proc->header);
496     }
497
498     lock_acquire_shared(&proc->handle_table_lock);
499
500     if (handle >= proc->handle_table_size)
501     {
502         lock_release(&proc->handle_table_lock);
503         dereference(&proc->header);
504         return ERR_INVALID;
505     }
506
507     object_t *obj = proc->handle_table[handle].obj;
508     access_flags_t access_flags = proc->handle_table[handle].access_flags;
509     if (obj == NULL)
510     {
511         lock_release(&proc->handle_table_lock);
512         dereference(&proc->header);
513         return ERR_INVALID;
514     }
515
516     reference(obj);
517
518     lock_release(&proc->handle_table_lock);
519     dereference(&proc->header);
520
521     if (dest_process != INVALID_HANDLE)
522     {
523         if (!reference_by_handle(source_process, OBJECT_PROCESS, (object_t**)&proc))
524         {
525             dereference(obj);
526             return ERR_INVALID;
527         }
528     }
529     else
530     {
531         proc = get_previous_mode() == USER_MODE ? get_current_process() : kernel_process;
532         reference(&proc->header);
533     }
534
535     process_t *old_process = switch_process(proc);
536     dword_t ret = open_object(obj, access_flags, &safe_handle);
537     switch_process(old_process);
538
539     dereference(&proc->header);
540     dereference(obj);
541
542     EH_TRY
543     {
544         *duplicate = safe_handle;
545     }
546     EH_CATCH
547     {
548         ret = ERR_BADPTR;
549     }
550     EH_DONE;
551
552     return ret;
553 }
554
555 dword_t enum_objects_by_type(object_type_t type, object_t **object)
556 {
557     dword_t ret = ERR_SUCCESS;
558     list_entry_t *ptr;
559     object_t *previous = *object;
560
561     lock_acquire(&obj_lock);
562
563     if (previous == NULL) ptr = objects_by_type[type].next;
564     else ptr = previous->by_type_list.next;
565
566     if (ptr != &objects_by_type[type])
567     {
568         *object = CONTAINER_OF(ptr, object_t, by_type_list);
569         ret = ERR_SUCCESS;
570     }
571     else
572     {
573         *object = NULL;
574         ret = ERR_NOMORE;
575     }
576
577     lock_release(&obj_lock);
578
579     if (*object) reference(*object);
580     if (previous) dereference(previous);
581     return ret;
582 }
583
584 static sysret_t wait_for_objects(const handle_t *handles, void * const *parameters, size_t count, timeout_t timeout, wait_condition_type_t condition_type)
585 {
586     dword_t ret = ERR_SUCCESS;
587     object_t **objects = NULL;
588     wait_condition_t *condition = NULL;
589     const handle_t *safe_handles;
590     void * const *safe_parameters = NULL;
591     processor_mode_t previous_mode = get_previous_mode();
592     if (count == 0) return ERR_INVALID;
593
594     if (previous_mode == USER_MODE)
595     {
596         if (!check_usermode(handles, count * sizeof(handle_t))) return ERR_BADPTR;
597         if (parameters && !check_usermode(parameters, count * sizeof(void*))) return ERR_BADPTR;
598
599         safe_handles = calloc(count, sizeof(handle_t));
600         if (safe_handles == NULL) return ERR_NOMEMORY;
601
602         if (parameters)
603         {
604             safe_parameters = calloc(count, sizeof(void*));
605             if (safe_parameters == NULL)
606             {
607                 free((void*)safe_handles);
608                 return ERR_NOMEMORY;
609             }
610         }
611
612         EH_TRY
613         {
614             memcpy((handle_t*)safe_handles, handles, count * sizeof(handle_t));
615             if (safe_parameters) memcpy((void*)safe_parameters, parameters, count * sizeof(void*));
616         }
617         EH_CATCH
618         {
619             free((void*)safe_handles);
620             if (safe_parameters) free((void*)safe_parameters);
621             EH_ESCAPE(return ERR_BADPTR);
622         }
623         EH_DONE;
624     }
625     else
626     {
627         safe_handles = handles;
628         safe_parameters = parameters;
629     }
630
631     if (!(objects = calloc(count, sizeof(object_t*))))
632     {
633         ret = ERR_NOMEMORY;
634         goto cleanup;
635     }
636
637     size_t i;
638     for (i = 0; i < count; i++)
639     {
640         if (!reference_by_handle(safe_handles[i], OBJECT_ANY_TYPE, &objects[i]))
641         {
642             ret = ERR_INVALID;
643             goto cleanup;
644         }
645     }
646
647     if (!(condition = malloc(sizeof(wait_condition_t) + count * sizeof(wait_condition_t*))))
648     {
649         ret = ERR_NOMEMORY;
650         goto cleanup;
651     }
652
653     memset(condition, 0, sizeof(wait_condition_t) + (count + 1) * sizeof(wait_condition_t*));
654     condition->type = condition_type;
655
656     for (i = 0; i < count; i++)
657     {
658         if (!type_info[objects[i]->type].pre_wait)
659         {
660             ret = ERR_INVALID;
661             goto cleanup;
662         }
663
664         wait_condition_t *cond = malloc(sizeof(wait_condition_t));
665         if (!cond)
666         {
667             ret = ERR_NOMEMORY;
668             goto cleanup;
669         }
670
671         ret = type_info[objects[i]->type].pre_wait(objects[i], safe_parameters[i], cond);
672         if (ret != ERR_SUCCESS)
673         {
674             free(cond);
675             goto cleanup;
676         }
677
678         condition->conditions[i] = cond;
679     }
680
681     wait_result_t result = scheduler_wait(condition, timeout);
682     if (result == WAIT_CANCELED) ret = ERR_CANCELED;
683     else if (result == WAIT_TIMED_OUT) ret = ERR_TIMEOUT;
684
685     for (i = 0; i < count; i++)
686     {
687         if (type_info[objects[i]->type].post_wait)
688         {
689             type_info[objects[i]->type].post_wait(objects[i], safe_parameters ? safe_parameters[i] : NULL, result);
690         }
691     }
692
693 cleanup:
694     if (condition)
695     {
696         for (i = 0; i < count; i++) if (condition->conditions[i]) free(condition->conditions[i]);
697         free(condition);
698     }
699
700     if (objects)
701     {
702         for (i = 0; i < count; i++) if (objects[i]) dereference(objects[i]);
703         free(objects);
704     }
705
706     if (previous_mode == USER_MODE)
707     {
708         free((void*)safe_handles);
709         if (safe_parameters) free((void*)safe_parameters);
710     }
711
712     return ret;
713 }
714
715 sysret_t syscall_wait_for_one(handle_t handle, void *parameter, timeout_t timeout)
716 {
717     dword_t ret = ERR_SUCCESS;
718
719     object_t *object;
720     if (!reference_by_handle(handle, OBJECT_ANY_TYPE, &object)) return ERR_INVALID;
721
722     if (!type_info[object->type].pre_wait)
723     {
724         dereference(object);
725         return ERR_INVALID;
726     }
727
728     wait_condition_t condition;
729     ret = type_info[object->type].pre_wait(object, parameter, &condition);
730     if (ret != ERR_SUCCESS) return ret;
731
732     wait_result_t result = scheduler_wait(&condition, timeout);
733     dereference(object);
734
735     switch (result)
736     {
737     case WAIT_CONDITION_HIT:
738         return ERR_SUCCESS;
739     case WAIT_CANCELED:
740         return ERR_CANCELED;
741     case WAIT_TIMED_OUT:
742         return ERR_TIMEOUT;
743
744     default:
745         KERNEL_CRASH("Unexpected scheduler wait result");
746         return ERR_INVALID;
747     }
748 }
749
750 sysret_t syscall_wait_for_any(const handle_t *handles, void * const *parameters, size_t count, timeout_t timeout)
751 {
752     return wait_for_objects(handles, parameters, count, timeout, WAIT_GROUP_ANY);
753 }
754
755 sysret_t syscall_wait_for_all(const handle_t *handles, void * const *parameters, size_t count, timeout_t timeout)
756 {
757      return wait_for_objects(handles, parameters, count, timeout, WAIT_GROUP_ALL);
758 }
759
760 void object_init(void)
761 {
762     list_init_array(named_objects, sizeof(named_objects) / sizeof(*named_objects));
763     list_init_array(objects_by_type, sizeof(objects_by_type) / sizeof(*objects_by_type));
764 }