/* Copyright (C) 2014 Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* * shim_handle.c * * This file contains codes to maintain bookkeeping for handles in library OS. */ #include #include #include #include #include #include #include static struct shim_lock handle_mgr_lock; #define HANDLE_MGR_ALLOC 32 #define SYSTEM_LOCK() lock(&handle_mgr_lock) #define SYSTEM_UNLOCK() unlock(&handle_mgr_lock) #define PAGE_SIZE allocsize #define OBJ_TYPE struct shim_handle #include static MEM_MGR handle_mgr = NULL; #define INIT_HANDLE_MAP_SIZE 32 //#define DEBUG_REF static inline int init_tty_handle(struct shim_handle* hdl, bool write) { struct shim_dentry* dent = NULL; int ret; struct shim_thread* cur_thread = get_cur_thread(); /* XXX: Try getting the root FS from current thread? */ assert(cur_thread); assert(cur_thread->root); if ((ret = path_lookupat(NULL, "/dev/tty", LOOKUP_OPEN, &dent, NULL)) < 0) return ret; int flags = (write ? O_WRONLY : O_RDONLY) | O_APPEND; struct shim_mount* fs = dent->fs; ret = fs->d_ops->open(hdl, dent, flags); if (ret < 0) return ret; set_handle_fs(hdl, fs); hdl->dentry = dent; hdl->flags = O_RDWR | O_APPEND | 0100000; size_t size; char* path = dentry_get_path(dent, true, &size); if (path) qstrsetstr(&hdl->path, path, size); else qstrsetstr(&hdl->path, "/dev/tty", 8); return 0; } static inline int init_exec_handle(struct shim_thread* thread) { if (!PAL_CB(executable)) return 0; struct shim_handle* exec = get_new_handle(); if (!exec) return -ENOMEM; qstrsetstr(&exec->uri, PAL_CB(executable), strlen(PAL_CB(executable))); exec->type = TYPE_FILE; exec->flags = O_RDONLY; exec->acc_mode = MAY_READ; struct shim_mount* fs = find_mount_from_uri(PAL_CB(executable)); if (fs) { const char* p = PAL_CB(executable) + fs->uri.len; /* * Lookup for PAL_CB(executable) needs to be done under a given * mount point. which requires a relative path name. * On the other hand, the one in manifest file can be absolute path. */ while (*p == '/') { p++; } path_lookupat(fs->root, p, 0, &exec->dentry, fs); set_handle_fs(exec, fs); if (exec->dentry) { size_t len; const char* path = dentry_get_path(exec->dentry, true, &len); qstrsetstr(&exec->path, path, len); } put_mount(fs); } else { set_handle_fs(exec, &chroot_builtin_fs); } lock(&thread->lock); thread->exec = exec; unlock(&thread->lock); return 0; } static struct shim_handle_map* get_new_handle_map(FDTYPE size); PAL_HANDLE shim_stdio = NULL; static int __set_new_fd_handle(struct shim_fd_handle** fdhdl, FDTYPE fd, struct shim_handle* hdl, int flags); static struct shim_handle_map* __enlarge_handle_map(struct shim_handle_map* map, FDTYPE size); int init_handle(void) { create_lock(&handle_mgr_lock); handle_mgr = create_mem_mgr(init_align_up(HANDLE_MGR_ALLOC)); if (!handle_mgr) return -ENOMEM; return 0; } int init_important_handles(void) { struct shim_thread* thread = get_cur_thread(); if (thread->handle_map) goto done; struct shim_handle_map* handle_map = get_cur_handle_map(thread); if (!handle_map) { handle_map = get_new_handle_map(INIT_HANDLE_MAP_SIZE); if (!handle_map) return -ENOMEM; set_handle_map(thread, handle_map); } lock(&handle_map->lock); if (handle_map->fd_size < 3) { if (!__enlarge_handle_map(handle_map, INIT_HANDLE_MAP_SIZE)) { unlock(&handle_map->lock); return -ENOMEM; } } struct shim_handle* hdl = NULL; int ret; for (int fd = 0; fd < 3; fd++) if (!HANDLE_ALLOCATED(handle_map->map[fd])) { if (!hdl) { hdl = get_new_handle(); if (!hdl) return -ENOMEM; if ((ret = init_tty_handle(hdl, fd)) < 0) { put_handle(hdl); return ret; } } else { get_handle(hdl); } __set_new_fd_handle(&handle_map->map[fd], fd, hdl, 0); put_handle(hdl); if (fd != 1) hdl = NULL; } else { if (fd == 1) hdl = handle_map->map[fd]->handle; } if (handle_map->fd_top == FD_NULL || handle_map->fd_top < 2) handle_map->fd_top = 2; unlock(&handle_map->lock); done: init_exec_handle(thread); return 0; } struct shim_handle* __get_fd_handle(FDTYPE fd, int* flags, struct shim_handle_map* map) { struct shim_fd_handle* fd_handle = NULL; if (map->fd_top != FD_NULL && fd <= map->fd_top) { fd_handle = map->map[fd]; if (!HANDLE_ALLOCATED(fd_handle)) return NULL; if (flags) *flags = fd_handle->flags; return fd_handle->handle; } return NULL; } struct shim_handle* get_fd_handle(FDTYPE fd, int* flags, struct shim_handle_map* map) { if (!map) map = get_cur_handle_map(NULL); struct shim_handle* hdl = NULL; lock(&map->lock); if ((hdl = __get_fd_handle(fd, flags, map))) get_handle(hdl); unlock(&map->lock); return hdl; } struct shim_handle* __detach_fd_handle(struct shim_fd_handle* fd, int* flags, struct shim_handle_map* map) { struct shim_handle* handle = NULL; if (HANDLE_ALLOCATED(fd)) { int vfd = fd->vfd; handle = fd->handle; if (flags) *flags = fd->flags; fd->vfd = FD_NULL; fd->handle = NULL; fd->flags = 0; if (vfd == map->fd_top) do { map->fd_top = vfd ? vfd - 1 : FD_NULL; vfd--; } while (vfd >= 0 && !HANDLE_ALLOCATED(map->map[vfd])); } return handle; } struct shim_handle* detach_fd_handle(FDTYPE fd, int* flags, struct shim_handle_map* handle_map) { struct shim_handle* handle = NULL; if (!handle_map && !(handle_map = get_cur_handle_map(NULL))) return NULL; lock(&handle_map->lock); if (fd < handle_map->fd_size) handle = __detach_fd_handle(handle_map->map[fd], flags, handle_map); unlock(&handle_map->lock); return handle; } struct shim_handle* get_new_handle(void) { struct shim_handle* new_handle = get_mem_obj_from_mgr_enlarge(handle_mgr, size_align_up(HANDLE_MGR_ALLOC)); if (!new_handle) return NULL; memset(new_handle, 0, sizeof(struct shim_handle)); REF_SET(new_handle->ref_count, 1); create_lock(&new_handle->lock); new_handle->owner = cur_process.vmid; INIT_LISTP(&new_handle->epolls); return new_handle; } static int __set_new_fd_handle(struct shim_fd_handle** fdhdl, FDTYPE fd, struct shim_handle* hdl, int flags) { struct shim_fd_handle* new_handle = *fdhdl; if (!new_handle) { new_handle = malloc(sizeof(struct shim_fd_handle)); if (!new_handle) return -ENOMEM; *fdhdl = new_handle; } new_handle->vfd = fd; new_handle->flags = flags; get_handle(hdl); new_handle->handle = hdl; return 0; } int set_new_fd_handle(struct shim_handle* hdl, int flags, struct shim_handle_map* handle_map) { FDTYPE fd = 0; int new_size = 0; int ret = 0; if (!handle_map && !(handle_map = get_cur_handle_map(NULL))) return -EBADF; lock(&handle_map->lock); if (!handle_map->map || handle_map->fd_size < INIT_HANDLE_MAP_SIZE) new_size = INIT_HANDLE_MAP_SIZE; if (!handle_map->map) goto extend; if (handle_map->fd_top != FD_NULL) do { ++fd; if (fd == handle_map->fd_size) { new_size = handle_map->fd_size < new_size ? new_size : handle_map->fd_size * 2; extend: if (!__enlarge_handle_map(handle_map, new_size)) { ret = -ENOMEM; goto out; } } } while (handle_map->fd_top != FD_NULL && fd <= handle_map->fd_top && HANDLE_ALLOCATED(handle_map->map[fd])); if (handle_map->fd_top == FD_NULL || fd > handle_map->fd_top) handle_map->fd_top = fd; ret = __set_new_fd_handle(&handle_map->map[fd], fd, hdl, flags); if (ret < 0) { if (fd == handle_map->fd_top) handle_map->fd_top = fd ? fd - 1 : FD_NULL; } else ret = fd; out: unlock(&handle_map->lock); return ret; } int set_new_fd_handle_by_fd(FDTYPE fd, struct shim_handle* hdl, int flags, struct shim_handle_map* handle_map) { int new_size = 0; int ret = 0; if (!handle_map && !(handle_map = get_cur_handle_map(NULL))) return -EBADF; lock(&handle_map->lock); if (!handle_map->map || handle_map->fd_size < INIT_HANDLE_MAP_SIZE) new_size = INIT_HANDLE_MAP_SIZE; if (!handle_map->map) goto extend; if (fd >= handle_map->fd_size) { new_size = handle_map->fd_size < new_size ? new_size : handle_map->fd_size; extend: while (new_size <= fd) new_size *= 2; if (!__enlarge_handle_map(handle_map, new_size)) { ret = -ENOMEM; goto out; } } if (handle_map->fd_top != FD_NULL && fd <= handle_map->fd_top && HANDLE_ALLOCATED(handle_map->map[fd])) { ret = -EBADF; goto out; } if (handle_map->fd_top == FD_NULL || fd > handle_map->fd_top) handle_map->fd_top = fd; struct shim_fd_handle* new_handle = handle_map->map[fd]; if (!new_handle) { new_handle = malloc(sizeof(struct shim_fd_handle)); if (!new_handle) { ret = -ENOMEM; goto out; } handle_map->map[fd] = new_handle; } ret = __set_new_fd_handle(&handle_map->map[fd], fd, hdl, flags); if (ret < 0) { if (fd == handle_map->fd_top) handle_map->fd_top = fd ? fd - 1 : FD_NULL; } else ret = fd; out: unlock(&handle_map->lock); return ret; } void flush_handle(struct shim_handle* hdl) { if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->flush) hdl->fs->fs_ops->flush(hdl); } static inline __attribute__((unused)) const char* __handle_name(struct shim_handle* hdl) { if (!qstrempty(&hdl->path)) return qstrgetstr(&hdl->path); if (!qstrempty(&hdl->uri)) return qstrgetstr(&hdl->uri); if (hdl->fs_type[0]) return hdl->fs_type; return "(unknown)"; } void get_handle(struct shim_handle* hdl) { #ifdef DEBUG_REF int ref_count = REF_INC(hdl->ref_count); debug("get handle %p(%s) (ref_count = %d)\n", hdl, __handle_name(hdl), ref_count); #else REF_INC(hdl->ref_count); #endif } static void destroy_handle(struct shim_handle* hdl) { destroy_lock(&hdl->lock); if (memory_migrated(hdl)) memset(hdl, 0, sizeof(struct shim_handle)); else free_mem_obj_to_mgr(handle_mgr, hdl); } extern int delete_from_epoll_handles(struct shim_handle* handle); void put_handle(struct shim_handle* hdl) { int ref_count = REF_DEC(hdl->ref_count); #ifdef DEBUG_REF debug("put handle %p(%s) (ref_count = %d)\n", hdl, __handle_name(hdl), ref_count); #endif if (!ref_count) { if (hdl->type == TYPE_DIR) { struct shim_dir_handle* dir = &hdl->dir_info; if (dir->dot) { put_dentry(dir->dot); dir->dot = NULL; } if (dir->dotdot) { put_dentry(dir->dotdot); dir->dotdot = NULL; } if (dir->ptr != (void*)-1) { while (dir->ptr && *dir->ptr) { struct shim_dentry* dent = *dir->ptr; put_dentry(dent); *(dir->ptr++) = NULL; } } } else { if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->close) hdl->fs->fs_ops->close(hdl); } delete_from_epoll_handles(hdl); if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->hput) hdl->fs->fs_ops->hput(hdl); qstrfree(&hdl->path); qstrfree(&hdl->uri); if (hdl->pal_handle) { #ifdef DEBUG_REF debug("handle %p closes PAL handle %p\n", hdl, hdl->pal_handle); #endif DkObjectClose(hdl->pal_handle); hdl->pal_handle = NULL; } if (hdl->dentry) put_dentry(hdl->dentry); if (hdl->fs) put_mount(hdl->fs); destroy_handle(hdl); } } off_t get_file_size(struct shim_handle* hdl) { if (!hdl->fs || !hdl->fs->fs_ops) return -EINVAL; if (hdl->fs->fs_ops->poll) return hdl->fs->fs_ops->poll(hdl, FS_POLL_SZ); if (hdl->fs->fs_ops->hstat) { struct stat stat; int ret = hdl->fs->fs_ops->hstat(hdl, &stat); if (ret < 0) return ret; return stat.st_size; } return 0; } void dup_fd_handle(struct shim_handle_map* map, const struct shim_fd_handle* old, struct shim_fd_handle* new) { struct shim_handle* replaced = NULL; lock(&map->lock); if (old->vfd != FD_NULL) { get_handle(old->handle); replaced = new->handle; new->handle = old->handle; } unlock(&map->lock); if (replaced) put_handle(replaced); } static struct shim_handle_map* get_new_handle_map(FDTYPE size) { struct shim_handle_map* handle_map = calloc(1, sizeof(struct shim_handle_map)); if (!handle_map) return NULL; handle_map->map = calloc(size, sizeof(struct shim_fd_handle)); if (!handle_map->map) { free(handle_map); return NULL; } handle_map->fd_top = FD_NULL; handle_map->fd_size = size; create_lock(&handle_map->lock); return handle_map; } static struct shim_handle_map* __enlarge_handle_map(struct shim_handle_map* map, FDTYPE size) { if (size <= map->fd_size) return map; struct shim_fd_handle** new_map = calloc(size, sizeof(new_map[0])); if (!new_map) return NULL; memcpy(new_map, map->map, map->fd_size * sizeof(new_map[0])); memset(new_map + map->fd_size, 0, (size - map->fd_size) * sizeof(new_map[0])); free(map->map); map->map = new_map; map->fd_size = size; return map; } int dup_handle_map(struct shim_handle_map** new, struct shim_handle_map* old_map) { lock(&old_map->lock); /* allocate a new handle mapping with the same size as the old one */ struct shim_handle_map* new_map = get_new_handle_map(old_map->fd_size); if (!new_map) return -ENOMEM; new_map->fd_top = old_map->fd_top; if (old_map->fd_top == FD_NULL) goto done; for (int i = 0; i <= old_map->fd_top; i++) { struct shim_fd_handle* fd_old = old_map->map[i]; struct shim_fd_handle* fd_new; /* now we go through the handle map and reassign each of them being allocated */ if (HANDLE_ALLOCATED(fd_old)) { /* first, get the handle to prevent it from being deleted */ struct shim_handle* hdl = fd_old->handle; get_handle(hdl); fd_new = malloc(sizeof(struct shim_fd_handle)); if (!fd_new) { for (int j = 0; j < i; j++) { put_handle(new_map->map[j]->handle); free(new_map->map[j]); } unlock(&old_map->lock); *new = NULL; free(new_map); return -ENOMEM; } /* DP: I assume we really need a deep copy of the handle map? */ new_map->map[i] = fd_new; fd_new->vfd = fd_old->vfd; fd_new->handle = hdl; fd_new->flags = fd_old->flags; } } done: unlock(&old_map->lock); *new = new_map; return 0; } void get_handle_map(struct shim_handle_map* map) { REF_INC(map->ref_count); } void put_handle_map(struct shim_handle_map* map) { int ref_count = REF_DEC(map->ref_count); if (!ref_count) { if (map->fd_top == FD_NULL) goto done; for (int i = 0; i <= map->fd_top; i++) { if (!map->map[i]) continue; if (map->map[i]->vfd != FD_NULL) { struct shim_handle* handle = map->map[i]->handle; if (handle) put_handle(handle); } free(map->map[i]); } done: destroy_lock(&map->lock); free(map->map); free(map); } } int flush_handle_map(struct shim_handle_map* map) { get_handle_map(map); lock(&map->lock); if (map->fd_top == FD_NULL) goto done; /* now we go through the handle map and flush each handle */ for (int i = 0; i <= map->fd_top; i++) { if (!HANDLE_ALLOCATED(map->map[i])) continue; struct shim_handle* handle = map->map[i]->handle; if (handle) flush_handle(handle); } done: unlock(&map->lock); put_handle_map(map); return 0; } int walk_handle_map(int (*callback)(struct shim_fd_handle*, struct shim_handle_map*), struct shim_handle_map* map) { int ret = 0; lock(&map->lock); if (map->fd_top == FD_NULL) goto done; for (int i = 0; i <= map->fd_top; i++) { if (!HANDLE_ALLOCATED(map->map[i])) continue; if ((ret = (*callback)(map->map[i], map)) < 0) break; } done: unlock(&map->lock); return ret; } BEGIN_CP_FUNC(handle) { assert(size == sizeof(struct shim_handle)); struct shim_handle* hdl = (struct shim_handle*)obj; struct shim_handle* new_hdl = NULL; ptr_t off = GET_FROM_CP_MAP(obj); if (!off) { off = ADD_CP_OFFSET(sizeof(struct shim_handle)); ADD_TO_CP_MAP(obj, off); new_hdl = (struct shim_handle*)(base + off); lock(&hdl->lock); struct shim_mount* fs = hdl->fs; *new_hdl = *hdl; if (fs && fs->fs_ops && fs->fs_ops->checkout) fs->fs_ops->checkout(new_hdl); new_hdl->dentry = NULL; REF_SET(new_hdl->ref_count, 0); clear_lock(&new_hdl->lock); DO_CP_IN_MEMBER(qstr, new_hdl, path); DO_CP_IN_MEMBER(qstr, new_hdl, uri); if (fs && hdl->dentry) { DO_CP_MEMBER(mount, hdl, new_hdl, fs); } else { new_hdl->fs = NULL; } if (hdl->dentry) DO_CP_MEMBER(dentry, hdl, new_hdl, dentry); if (new_hdl->pal_handle) { struct shim_palhdl_entry* entry; DO_CP(palhdl, hdl->pal_handle, &entry); entry->uri = &new_hdl->uri; entry->phandle = &new_hdl->pal_handle; } if (hdl->type == TYPE_EPOLL) DO_CP(epoll_fd, &hdl->info.epoll.fds, &new_hdl->info.epoll.fds); INIT_LISTP(&new_hdl->epolls); unlock(&hdl->lock); ADD_CP_FUNC_ENTRY(off); } else { new_hdl = (struct shim_handle*)(base + off); } if (objp) *objp = (void*)new_hdl; } END_CP_FUNC(handle) BEGIN_RS_FUNC(handle) { struct shim_handle* hdl = (void*)(base + GET_CP_FUNC_ENTRY()); __UNUSED(offset); CP_REBASE(hdl->fs); CP_REBASE(hdl->dentry); CP_REBASE(hdl->epolls); create_lock(&hdl->lock); if (!hdl->fs) { assert(hdl->fs_type); search_builtin_fs(hdl->fs_type, &hdl->fs); if (!hdl->fs) return -EINVAL; } if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->checkin) hdl->fs->fs_ops->checkin(hdl); DEBUG_RS("path=%s,type=%s,uri=%s,flags=%03o", qstrgetstr(&hdl->path), hdl->fs_type, qstrgetstr(&hdl->uri), hdl->flags); } END_RS_FUNC(handle) BEGIN_CP_FUNC(fd_handle) { assert(size == sizeof(struct shim_fd_handle)); struct shim_fd_handle* fdhdl = (struct shim_fd_handle*)obj; struct shim_fd_handle* new_fdhdl = NULL; ptr_t off = ADD_CP_OFFSET(sizeof(struct shim_fd_handle)); new_fdhdl = (struct shim_fd_handle*)(base + off); memcpy(new_fdhdl, fdhdl, sizeof(struct shim_fd_handle)); DO_CP(handle, fdhdl->handle, &new_fdhdl->handle); ADD_CP_FUNC_ENTRY(off); if (objp) *objp = (void*)new_fdhdl; } END_CP_FUNC_NO_RS(fd_handle) BEGIN_CP_FUNC(handle_map) { assert(size >= sizeof(struct shim_handle_map)); struct shim_handle_map* handle_map = (struct shim_handle_map*)obj; struct shim_handle_map* new_handle_map = NULL; struct shim_fd_handle** ptr_array; lock(&handle_map->lock); int fd_size = handle_map->fd_top != FD_NULL ? handle_map->fd_top + 1 : 0; size = sizeof(struct shim_handle_map) + (sizeof(struct shim_fd_handle*) * fd_size); ptr_t off = GET_FROM_CP_MAP(obj); if (!off) { off = ADD_CP_OFFSET(size); new_handle_map = (struct shim_handle_map*)(base + off); memcpy(new_handle_map, handle_map, sizeof(struct shim_handle_map)); ptr_array = (void*)new_handle_map + sizeof(struct shim_handle_map); new_handle_map->fd_size = fd_size; new_handle_map->map = fd_size ? ptr_array : NULL; REF_SET(new_handle_map->ref_count, 0); clear_lock(&new_handle_map->lock); for (int i = 0; i < fd_size; i++) { if (HANDLE_ALLOCATED(handle_map->map[i])) DO_CP(fd_handle, handle_map->map[i], &ptr_array[i]); else ptr_array[i] = NULL; } ADD_CP_FUNC_ENTRY(off); } else { new_handle_map = (struct shim_handle_map*)(base + off); } unlock(&handle_map->lock); if (objp) *objp = (void*)new_handle_map; } END_CP_FUNC(handle_map) BEGIN_RS_FUNC(handle_map) { struct shim_handle_map* handle_map = (void*)(base + GET_CP_FUNC_ENTRY()); __UNUSED(offset); CP_REBASE(handle_map->map); assert(handle_map->map); DEBUG_RS("size=%d,top=%d", handle_map->fd_size, handle_map->fd_top); create_lock(&handle_map->lock); lock(&handle_map->lock); if (handle_map->fd_top != FD_NULL) for (int i = 0; i <= handle_map->fd_top; i++) { CP_REBASE(handle_map->map[i]); if (HANDLE_ALLOCATED(handle_map->map[i])) { CP_REBASE(handle_map->map[i]->handle); struct shim_handle* hdl = handle_map->map[i]->handle; assert(hdl); get_handle(hdl); DEBUG_RS("[%d]%s", i, qstrempty(&hdl->uri) ? hdl->fs_type : qstrgetstr(&hdl->uri)); } } unlock(&handle_map->lock); } END_RS_FUNC(handle_map)