diff options
Diffstat (limited to 'libstore/file.c')
-rw-r--r-- | libstore/file.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/libstore/file.c b/libstore/file.c new file mode 100644 index 00000000..e952a91e --- /dev/null +++ b/libstore/file.c @@ -0,0 +1,275 @@ +/* File store backend + + Copyright (C) 1995, 96, 97, 98 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.ai.mit.edu> + This file is part of the GNU Hurd. + + The GNU Hurd 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. + + The GNU Hurd 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <hurd.h> + +#include <hurd/io.h> + +#include "store.h" + +static error_t +file_read (struct store *store, + off_t addr, size_t index, size_t amount, void **buf, size_t *len) +{ + size_t bsize = store->block_size; + return io_read (store->port, (char **)buf, len, addr * bsize, amount); +} + +static error_t +file_write (struct store *store, + off_t addr, size_t index, void *buf, size_t len, size_t *amount) +{ + size_t bsize = store->block_size; + return io_write (store->port, buf, len, addr * bsize, amount); +} + +static error_t +file_decode (struct store_enc *enc, const struct store_class *const *classes, + struct store **store) +{ + return store_std_leaf_decode (enc, _store_file_create, store); +} + +static error_t +file_open (const char *name, int flags, + const struct store_class *const *classes, + struct store **store) +{ + return store_file_open (name, flags, store); +} + +static error_t +fiopen (const char *name, file_t *file, int *mod_flags) +{ + if (*mod_flags & STORE_HARD_READONLY) + *file = file_name_lookup (name, O_RDONLY, 0); + else + { + *file = file_name_lookup (name, O_RDWR, 0); + if (*file == MACH_PORT_NULL + && (errno == EACCES || errno == EROFS)) + { + *file = file_name_lookup (name, O_RDONLY, 0); + if (*file != MACH_PORT_NULL) + *mod_flags |= STORE_HARD_READONLY; + } + else if (*file != MACH_PORT_NULL) + *mod_flags &= ~STORE_HARD_READONLY; + } + return *file == MACH_PORT_NULL ? errno : 0; +} + +static void +ficlose (struct store *store) +{ + mach_port_deallocate (mach_task_self (), store->port); + store->port = MACH_PORT_NULL; +} + +/* Return 0 if STORE's range is enforced by the filesystem, otherwise an + error. */ +static error_t +enforced (struct store *store) +{ + if (store->num_runs != 1 || store->runs[0].start != 0) + /* Can't enforce non-contiguous ranges, or one not starting at 0. */ + return EINVAL; + else + /* See if the the current (one) range is that the kernel is enforcing. */ + { + struct stat st; + error_t err = io_stat (store->port, &st); + + if (!err + && store->runs[0].length != (st.st_size >> store->log2_block_size)) + /* The single run is not the whole file. */ + err = EINVAL; + + return err; + } +} + +static error_t +file_set_flags (struct store *store, int flags) +{ + if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0) + /* Trying to set flags we don't support. */ + return EINVAL; + + if (! ((store->flags | flags) & STORE_INACTIVE)) + /* Currently active and staying that way, so we must be trying to set the + STORE_ENFORCED flag. */ + { + error_t err = enforced (store); + if (err) + return err; + } + + if (flags & STORE_INACTIVE) + ficlose (store); + + store->flags |= flags; /* When inactive, anything goes. */ + + return 0; +} + +static error_t +file_clear_flags (struct store *store, int flags) +{ + error_t err = 0; + if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0) + err = EINVAL; + if (!err && (flags & STORE_INACTIVE)) + err = store->name + ? fiopen (store->name, &store->port, &store->flags) + : ENOENT; + if (! err) + store->flags &= ~flags; + return err; +} + +static error_t +file_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj) +{ + error_t err; + mach_port_t rd_memobj, wr_memobj; + int ro = (store->flags & STORE_HARD_READONLY); + + if (store->num_runs != 1 || store->runs[0].start != 0) + return EOPNOTSUPP; + + if ((prot & VM_PROT_WRITE) && ro) + return EACCES; + + err = io_map (store->port, &rd_memobj, &wr_memobj); + if (err) + return err; + + *memobj = rd_memobj; + + if (ro && wr_memobj == MACH_PORT_NULL) + return 0; + else if (rd_memobj == wr_memobj) + { + if (rd_memobj != MACH_PORT_NULL) + mach_port_mod_refs (mach_task_self (), rd_memobj, + MACH_PORT_RIGHT_SEND, -1); + } + else + { + if (rd_memobj != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), rd_memobj); + if (wr_memobj != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), wr_memobj); + err = EOPNOTSUPP; + } + + return err; +} + +struct store_class +store_file_class = +{ + STORAGE_HURD_FILE, "file", file_read, file_write, + store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode, + file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map +}; + +static error_t +file_byte_read (struct store *store, + off_t addr, size_t index, size_t amount, + void **buf, size_t *len) +{ + return io_read (store->port, (char **)buf, len, addr, amount); +} + +static error_t +file_byte_write (struct store *store, + off_t addr, size_t index, void *buf, size_t len, + size_t *amount) +{ + return io_write (store->port, buf, len, addr, amount); +} + +struct store_class +store_file_byte_class = +{ + STORAGE_HURD_FILE, "file", file_byte_read, file_byte_write, + store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode, + file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map +}; + +/* Return a new store in STORE referring to the mach file FILE. Consumes + the send right FILE. */ +error_t +store_file_create (file_t file, int flags, struct store **store) +{ + struct store_run run; + struct stat stat; + error_t err = io_stat (file, &stat); + + if (err) + return err; + + run.start = 0; + run.length = stat.st_size; + + flags |= STORE_ENFORCED; /* 'cause it's the whole file. */ + + return _store_file_create (file, flags, 1, &run, 1, store); +} + +/* Like store_file_create, but doesn't query the file for information. */ +error_t +_store_file_create (file_t file, int flags, size_t block_size, + const struct store_run *runs, size_t num_runs, + struct store **store) +{ + if (block_size == 1) + return _store_create (&store_file_byte_class, + file, flags, 1, runs, num_runs, 0, store); + else + return _store_create (&store_file_class, + file, flags, block_size, runs, num_runs, 0, store); +} + +/* Open the file NAME, and return the corresponding store in STORE. */ +error_t +store_file_open (const char *name, int flags, struct store **store) +{ + file_t file; + error_t err = fiopen (name, &file, &flags); + if (! err) + { + err = store_file_create (file, flags, store); + if (! err) + { + err = store_set_name (*store, name); + if (err) + store_free (*store); + } + if (err) + mach_port_deallocate (mach_task_self (), file); + } + return err; +} |