📄 file.c
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* * file.c * * the one-domain per file implementation. the major * problem with this setup is that each domain needs * an input buffer as big as the maximum write length, * which in EROS curretnly is 64k. that means the * overhead per file is 64k, which is too high. * * we implmement the domain with a key to a red segment (biggerseg) * whose background key is a VCSK'd segment which holds * the file data. this red segment is initialized as * a parent node in the tree which holds our address space. * this means that it is automagically mapped into our * address space. * * to read, simply set the red segment's * background window, and set the IPC data pointer * to the correct address. to write, we do the same and * simply bcopy the new data to the correct address. if * that space doesn't exist, vcsk will allocate it for us. */#include <eros/target.h>#include <eros/Invoke.h>#include <eros/KeyBitsKey.h>#include <eros/NodeKey.h>#include <eros/StdKeyType.h>#include <eros/NumberKey.h>#include <eros/ProcessKey.h>#include <eros/SegKeeperInfo.h>#include <eros/ProcessState.h>#include <eros/Key.h>#include <domain/domdbg.h>#include <domain/FileKey.h>#include <domain/ConstructorKey.h>#include <domain/SpaceBankKey.h>#include "constituents.h"#define KR_CONSTIT 1#define KR_SELF 2#define KR_BANK 4#define KR_SCHED 5 /* key to seg with bgnd window */#define KR_MAP 6 /* key to seg with bgnd window */#define KR_RETURNER 7 /* we will eventually use this */#define KR_VCS 8#define KR_TMP 9#define KR_TMP2 10#define KR_OSTREAM 11#define BG_WIN_BLSS 5 /* size of the bg windows */#define BG_WIN_SIZE ( (uint64_t) (1u << ((BG_WIN_BLSS+1)*4)) )#define BG_WIN_MASK ( BG_WIN_SIZE - 1u )#define BG_WIN_ADDR 0x80000000u#define KR_RK0 28#define KR_RK1 29#define KR_RK2 30#define KR_RETURN 31#define dbg_init 0x1#define dbg_read 0x2#define dbg_write 0x4#define dbg_addr 0x8#define dbg_window 0x10#define dbg_grow 0x20/* Following should be an OR of some of the above */#define dbg_flags dbg_read#define DEBUG(x) if (dbg_##x & dbg_flags)const uint32_t __rt_stack_pages = 2;typedef struct { struct inode node; /* other inode data */ uint32_t blss; uint8_t rcvData[EROS_PAGE_SIZE]; uint64_t winStart; uint64_t winEnd;} file_state_t;voidbcopy(const void *from, void *to, uint32_t len){ /* * standard EROS bcopy */ uint8_t *fp = (uint8_t *) from; uint8_t *tp = (uint8_t *) to; while (len--) *tp++ = *fp++;}voidbzero(const void *buf, uint32_t len){ /* * standard EROS bcopy */ uint8_t *tp = (uint8_t *) buf; while (len--) *tp++ = 0;}voidbg_map_range (uint64_t lo, uint32_t hi, file_state_t *file_state, uint32_t *errorcode){ /* Maps a 1/2 gigabyte range in a background window which lands in our space at 0x8000 0000. Only shifts the window if it needs to. */ if (lo < file_state->winStart || hi > file_state->winEnd) { nk_value nkv; DEBUG(window) kdprintf(KR_OSTREAM, "Adjust the window range\n"); /* round offset down to a window multiple */ lo &= ~BG_WIN_MASK; nkv.value[0] = lo | 0x3; /* truncates */ nkv.value[1] = (lo >> 32); nkv.value[2] = 0; node_write_number(KR_MAP, 0, &nkv); lo += (uint64_t) BG_WIN_SIZE; nkv.value[0] = lo | 0x3; /* truncates */ nkv.value[1] = (lo >> 32); nkv.value[2] = 0; node_write_number(KR_MAP, 1, &nkv); file_state->winStart = lo; file_state->winEnd = lo + (2 * BG_WIN_SIZE); } DEBUG(window) kprintf(KR_OSTREAM, "Window covers [0x%08x%08x, 0x%08x0x%08x)\n", (uint32_t) (file_state->winStart >> 32), (uint32_t) file_state->winStart, (uint32_t) (file_state->winEnd >> 32), (uint32_t) file_state->winEnd);}#if 0uint32_tGrowFileTo(uint64_t newlen, file_state_t *file_state){ /* Initial VCS is only one page (blss==2), held in a container whose blss==3. If you hit the VCS in the intervening window (address > blss 2, smaller than blss 3, it will grow the segment for you. To grow the segment to a particular offset, then, simply READ the VCS at successive powers of 16 pages. The tricky part is to do this without blowing ourselves away or dorking the window if we don't have to. For this reason, we hung on to the VCS in KR_VCS. We are about to send it messages that look like the kernel fault messages it would normally receive. */ /* * BLSS is biased so need to add 1, then multiply by 4 since BLSS is a * base-16 shift and we're doing a base-2 shift. */ uint64_t top = 0x1llu << (4 * (file_state->blss + 1)); DEBUG(grow) kdprintf(KR_OSTREAM, "Grow the file\n"); while (top < newlen) { SegKeeperInfo ski; uint32_t result; Message msg; /* touch next byte to force grow */ ski.faultCode = FC_SegInvalidAddr; ski.offset[0] = top; ski.offset[1] = (top >> 32); ski.offset[2] = 0; msg.snd_code = OC_FAULT; msg.snd_key0 = KR_VOID; msg.snd_key1 = KR_VOID; msg.snd_key2 = KR_VOID; /* replaced by kernel for us */ msg.snd_key3 = KR_VOID; msg.snd_w1 = OCW_SegFault; msg.snd_invKey = KR_VCS; msg.snd_data = &ski; msg.snd_len = sizeof(ski); msg.rcv_len = 0; msg.rcv_key0 = KR_VOID; msg.rcv_key1 = KR_VOID; msg.rcv_key2 = KR_VOID; msg.rcv_key3 = KR_VOID; result = CALL(&msg); if (result != RC_OK) return result; file_state->blss++; top = 0x1llu << (4 * (file_state->blss + 1)); } DEBUG(grow) kdprintf(KR_OSTREAM, "Grow the file: done\n"); return RC_OK;}#endifintProcessRequest(Message *msg, file_state_t *file_state){ uint32_t errorcode; msg->snd_key0 = KR_VOID; /* send no keys until proven otherwise. */ switch (msg->rcv_code) { case OC_File_Write: { /* * call * w1 : lo offset * w2 : hi offset * len : write length * data : data * * return * w1 : len * * set background windows to a VCS and * bcopy the data at the given offset * in our address space * * FIX: what if the spacebank runs out of * space? how will we know this? */ /* shap wants 64 bit file size, but doesn't have time to debug test case right now */ uint64_t offset = msg->rcv_w1; uint32_t len = msg->rcv_len; DEBUG(write) kprintf (KR_OSTREAM, "FILE:Write: offset 0x%08x%08x len %08x\n", (uint32_t) (offset >> 32), (uint32_t) offset, len); /* Check this early -- no sense growing the file for a write that will fail anyway. */ if (len > EROS_PAGE_SIZE) { msg->snd_code = RC_RequestError; return 1; } #if 0 if (offset + len > file_state->node.i_sz) { uint32_t result = GrowFileTo(offset + len, file_state); if (result != RC_OK) { msg->snd_code = result; return 1; } }#endif /* adjust the mapping range if necessary */ bg_map_range(offset, offset + len, file_state, &errorcode); { uint8_t *addr = (uint8_t *) (uint32_t) (offset - file_state->winStart); addr += BG_WIN_ADDR; DEBUG(addr) kprintf(KR_OSTREAM, "FS: write: bcopy(0x%08x, 0x%08x, %d):\n", msg->rcv_data, addr, len); bcopy (msg->rcv_data, addr, len); } /* how about something like: */ if (offset + len > file_state->node.i_sz) file_state->node.i_sz = offset + len; msg->snd_w1 = len; msg->snd_code = RC_OK; return 1; } case OC_File_Inode:#if 0 /* shap says ignore this for now. It's not at all clear that we should be tracking this anyway, and to make it work right we really need to call the timer key on every read/write. */ /* * set some inode information; * pretty minimal right now * * call * w1 : mtime * w2 : ctime * w3 : atime */ node.i_mtime = msg->rcv_w1; node.i_ctime = msg->rcv_w2; node.i_atime = msg->rcv_w3; #endif break; case OC_File_Read: { /* * call * w1 : read offset lo * w2 : read offset hi * w3 : read len * * return * len : read len * * map the data into our addr space * by direct map of KR_MAP and using * a background window from KR_MAP */ uint64_t offset = msg->rcv_w1; /* ignore hi for now */ uint32_t len = msg->rcv_w3; DEBUG(read) kprintf (KR_OSTREAM, "FILE:Read: offset 0x%08x%08x len %08x\n", (uint32_t) (offset >> 32), (uint32_t) offset, len); if (offset >= file_state->node.i_sz) { msg->snd_code = RC_File_Length; return 1; } /* Check this early -- no sense adjusting the window for a write that will fail anyway. */ if (len > EROS_PAGE_SIZE) { msg->snd_code = RC_RequestError; return 1; } /* okay to return a short read, though: */ if (offset + len > file_state->node.i_sz) len = file_state->node.i_sz - offset; bg_map_range(offset, offset+len, file_state, &errorcode); { uint8_t *addr = (uint8_t *) (uint32_t) (offset - file_state->winStart); addr += BG_WIN_ADDR; msg->snd_data = addr; DEBUG(addr) kprintf(KR_OSTREAM, "FS: read: Return data from addr 0x%08x\n", addr); } msg->snd_len = len; msg->snd_code = RC_OK; return 1; } case OC_KeyType: /* check alleged keytype */ { msg->snd_code = RC_OK; msg->snd_w1 = AKT_File; return 1; } case OC_File_Map: { /* return our VCS key */ msg->snd_key0 = KR_VCS; msg->snd_code = RC_OK; return 1; } }; msg->snd_code = RC_UnknownRequest; return 1;}/* File object is initialized as a small program (occupies < 64K) and then constructs a bigger address space for itself (look, Ma, no hands!). On teardown, it reverses the process. */voidinit_file(file_state_t *file_state){ uint32_t result; node_copy(KR_CONSTIT, KC_OSTREAM, KR_OSTREAM); /* Do not bother to touch (validate mappings for) receive buffer, as we hand constructed those pages in CRT0 and therefore know that the path is writable. Since we know this, we are assured that we will not receive FC_PARMLACK */ DEBUG(init) kdprintf (KR_OSTREAM, "FILE: bzero stack (vcsf)\n"); /* Data structures need to be initialized! It happens that you know these stack pages are newly bought and therefore contain zeros. If you choose to rely on that it's okay but a comment here should say so! */ bzero(&file_state->node, sizeof(file_state->node)); /* Fabricate a new (empty) vcs to hold this file. The initial VCS is a single, zero page. This can be grown. */ node_copy(KR_CONSTIT, KC_VCSF, KR_VCS); file_state->blss = EROS_PAGE_BLSS; DEBUG(init) kdprintf (KR_OSTREAM, "Make VCS\n"); result = constructor_request(KR_VCS, KR_BANK, KR_SCHED, KR_VOID, KR_VCS); if (result != RC_OK) kdprintf (KR_OSTREAM, "FILE: init failed (vcsf)\n"); /* We need two nodes -- one for our new root and one to contain the background window keys. We will set the first to BLSS=7 the second to BLSS = BG_WIN_BLSS+1 (which is 6). */ result = spcbank_buy_nodes(KR_BANK, 2, KR_TMP, KR_MAP, KR_VOID); if (result != RC_OK) kdprintf (KR_OSTREAM, "FILE: init failed (spcbank)\n"); DEBUG(init) kdprintf (KR_OSTREAM, "Got VCS\n"); node_make_node_key(KR_TMP, 7, KR_TMP); /* Set up KR_MAP as a red node with VCS as BG seg */ node_make_node_key(KR_MAP, 1, KR_MAP); /* remember -- red node! */ { nk_value nkv;#error "Background segments no longer implemented."#ifdef KT_Wrapper nkv.value[0] = FORMAT_BACKGROUND WRAPPER_SET_BLSS(nkv, BG_WIN_BLSS + 1); /* none */ nkv.value[1] = 0; nkv.value[2] = 0; node_write_number(KR_MAP, RedSegFormat, &nkv);#else REDSEG_SET_INITIAL_SLOTS(nkv, 1); REDSEG_SET_SENDNODE(nkv, REDSEG_SEND_UNALTERED); REDSEG_SET_KPR_SLOT(nkv, RedSegFormat); REDSEG_SET_BG_SLOT(nkv, RedSegBackground); REDSEG_SET_BLSS(nkv, BG_WIN_BLSS + 1); /* none */ nkv.value[1] = 0; nkv.value[2] = 0; node_write_number(KR_MAP, RedSegFormat, &nkv);#endif#ifdef KT_Wrapper#error "Background segments with Wrappers don't work."#endif /* bg window, initial offset 0. */ nkv.value[0] = 3; node_write_number(KR_MAP, 0, &nkv); /* bg window, initial offset BG_WIN_SIZE. */ nkv.value[0] = BG_WIN_SIZE | 0x3; node_write_number(KR_MAP, 1, &nkv); file_state->winStart = 0x0; file_state->winEnd = 2 * BG_WIN_SIZE; /* bg segment is VCS */ node_swap(KR_MAP, RedSegBackground, KR_VCS, KR_VOID); } DEBUG(init) kdprintf (KR_OSTREAM, "built window keys\n"); /* Put the MAP node in at 0x8000 0000 */ node_swap(KR_TMP, 0x8, KR_MAP, KR_VOID); /* Fetch out our current address space */ process_copy(KR_SELF, ProcAddrSpace, KR_TMP2); /* Stick that in slot 0 of new addr space node */ node_swap(KR_TMP, 0, KR_TMP2, KR_VOID); DEBUG(init) kdprintf (KR_OSTREAM, "Install new spc\n"); /* Finally, we are ready to stick KR_TMP in as our own address space. Hold your breath, now... */ /* Fetch out our current address space */ process_swap(KR_SELF, ProcAddrSpace, KR_TMP, KR_VOID);}intmain(){ /* * initialize the state of the file domain * then set in motion the endless call-return * loop */ Message msg; file_state_t file_state; init_file(&file_state); /* A resume key to caller is sitting in slot 15. Fabricate a start key to hand them and do our first return: */ process_make_start_key(KR_SELF, 0, KR_TMP); msg.snd_invKey = KR_RETURN; msg.snd_key0 = KR_TMP; msg.snd_key1 = KR_VOID; msg.snd_key2 = KR_VOID; msg.snd_key3 = KR_VOID; msg.snd_data = 0; msg.snd_len = 0; msg.snd_code = 0; msg.snd_w1 = 0; msg.snd_w2 = 0; msg.snd_w3 = 0; msg.rcv_key0 = KR_VOID; msg.rcv_key1 = KR_VOID; msg.rcv_key2 = KR_VOID; msg.rcv_key3 = KR_RETURN; msg.rcv_data = file_state.rcvData; msg.rcv_code = 0; msg.rcv_w1 = 0; msg.rcv_w2 = 0; msg.rcv_w3 = 0; do { msg.rcv_len = EROS_PAGE_SIZE; RETURN(&msg); msg.snd_len = 0; /* unless it's a read, in which case ProcessRequest() will reset this. */ msg.snd_invKey = KR_RETURN; } while ( ProcessRequest(&msg, &file_state) ); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -