file_bd.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00041 #include <stdio.h>
00042 #include <unistd.h>
00043 #include <ipc/bd.h>
00044 #include <async.h>
00045 #include <as.h>
00046 #include <fibril_synch.h>
00047 #include <devmap.h>
00048 #include <sys/types.h>
00049 #include <sys/typefmt.h>
00050 #include <errno.h>
00051 #include <bool.h>
00052 #include <task.h>
00053 #include <macros.h>
00054 
00055 #define NAME "file_bd"
00056 
00057 #define DEFAULT_BLOCK_SIZE 512
00058 
00059 static size_t block_size;
00060 static aoff64_t num_blocks;
00061 static FILE *img;
00062 
00063 static devmap_handle_t devmap_handle;
00064 static fibril_mutex_t dev_lock;
00065 
00066 static void print_usage(void);
00067 static int file_bd_init(const char *fname);
00068 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
00069 static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf);
00070 static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
00071 
00072 int main(int argc, char **argv)
00073 {
00074         int rc;
00075         char *image_name;
00076         char *device_name;
00077 
00078         printf(NAME ": File-backed block device driver\n");
00079 
00080         block_size = DEFAULT_BLOCK_SIZE;
00081 
00082         ++argv; --argc;
00083         while (*argv != NULL && (*argv)[0] == '-') {
00084                 /* Option */
00085                 if (str_cmp(*argv, "-b") == 0) {
00086                         if (argc < 2) {
00087                                 printf("Argument missing.\n");
00088                                 print_usage();
00089                                 return -1;
00090                         }
00091 
00092                         rc = str_size_t(argv[1], NULL, 10, true, &block_size);
00093                         if (rc != EOK || block_size == 0) {
00094                                 printf("Invalid block size '%s'.\n", argv[1]);
00095                                 print_usage();
00096                                 return -1;
00097                         }
00098                         ++argv; --argc;
00099                 } else {
00100                         printf("Invalid option '%s'.\n", *argv);
00101                         print_usage();
00102                         return -1;
00103                 }
00104                 ++argv; --argc;
00105         }
00106 
00107         if (argc < 2) {
00108                 printf("Missing arguments.\n");
00109                 print_usage();
00110                 return -1;
00111         }
00112 
00113         image_name = argv[0];
00114         device_name = argv[1];
00115 
00116         if (file_bd_init(image_name) != EOK)
00117                 return -1;
00118 
00119         rc = devmap_device_register(device_name, &devmap_handle);
00120         if (rc != EOK) {
00121                 printf(NAME ": Unable to register device '%s'.\n",
00122                         device_name);
00123                 return rc;
00124         }
00125 
00126         printf(NAME ": Accepting connections\n");
00127         task_retval(0);
00128         async_manager();
00129 
00130         /* Not reached */
00131         return 0;
00132 }
00133 
00134 static void print_usage(void)
00135 {
00136         printf("Usage: " NAME " [-b <block_size>] <image_file> <device_name>\n");
00137 }
00138 
00139 static int file_bd_init(const char *fname)
00140 {
00141         int rc;
00142         long img_size;
00143 
00144         rc = devmap_driver_register(NAME, file_bd_connection);
00145         if (rc < 0) {
00146                 printf(NAME ": Unable to register driver.\n");
00147                 return rc;
00148         }
00149 
00150         img = fopen(fname, "rb+");
00151         if (img == NULL)
00152                 return EINVAL;
00153 
00154         if (fseek(img, 0, SEEK_END) != 0) {
00155                 fclose(img);
00156                 return EIO;
00157         }
00158 
00159         img_size = ftell(img);
00160         if (img_size < 0) {
00161                 fclose(img);
00162                 return EIO;
00163         }
00164 
00165         num_blocks = img_size / block_size;
00166 
00167         fibril_mutex_initialize(&dev_lock);
00168 
00169         return EOK;
00170 }
00171 
00172 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
00173 {
00174         void *fs_va = NULL;
00175         ipc_callid_t callid;
00176         ipc_call_t call;
00177         sysarg_t method;
00178         size_t comm_size;
00179         unsigned int flags;
00180         int retval;
00181         uint64_t ba;
00182         size_t cnt;
00183 
00184         /* Answer the IPC_M_CONNECT_ME_TO call. */
00185         async_answer_0(iid, EOK);
00186 
00187         if (!async_share_out_receive(&callid, &comm_size, &flags)) {
00188                 async_answer_0(callid, EHANGUP);
00189                 return;
00190         }
00191 
00192         fs_va = as_get_mappable_page(comm_size);
00193         if (fs_va == NULL) {
00194                 async_answer_0(callid, EHANGUP);
00195                 return;
00196         }
00197 
00198         (void) async_share_out_finalize(callid, fs_va);
00199 
00200         while (1) {
00201                 callid = async_get_call(&call);
00202                 method = IPC_GET_IMETHOD(call);
00203                 switch (method) {
00204                 case IPC_M_PHONE_HUNGUP:
00205                         /* The other side has hung up. */
00206                         async_answer_0(callid, EOK);
00207                         return;
00208                 case BD_READ_BLOCKS:
00209                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00210                             IPC_GET_ARG2(call));
00211                         cnt = IPC_GET_ARG3(call);
00212                         if (cnt * block_size > comm_size) {
00213                                 retval = ELIMIT;
00214                                 break;
00215                         }
00216                         retval = file_bd_read_blocks(ba, cnt, fs_va);
00217                         break;
00218                 case BD_WRITE_BLOCKS:
00219                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00220                             IPC_GET_ARG2(call));
00221                         cnt = IPC_GET_ARG3(call);
00222                         if (cnt * block_size > comm_size) {
00223                                 retval = ELIMIT;
00224                                 break;
00225                         }
00226                         retval = file_bd_write_blocks(ba, cnt, fs_va);
00227                         break;
00228                 case BD_GET_BLOCK_SIZE:
00229                         async_answer_1(callid, EOK, block_size);
00230                         continue;
00231                 case BD_GET_NUM_BLOCKS:
00232                         async_answer_2(callid, EOK, LOWER32(num_blocks),
00233                             UPPER32(num_blocks));
00234                         continue;
00235                 default:
00236                         retval = EINVAL;
00237                         break;
00238                 }
00239                 async_answer_0(callid, retval);
00240         }
00241 }
00242 
00244 static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf)
00245 {
00246         size_t n_rd;
00247         int rc;
00248 
00249         /* Check whether access is within device address bounds. */
00250         if (ba + cnt > num_blocks) {
00251                 printf(NAME ": Accessed blocks %" PRIuOFF64 "-%" PRIuOFF64 ", while "
00252                     "max block number is %" PRIuOFF64 ".\n", ba, ba + cnt - 1,
00253                     num_blocks - 1);
00254                 return ELIMIT;
00255         }
00256 
00257         fibril_mutex_lock(&dev_lock);
00258 
00259         clearerr(img);
00260         rc = fseek(img, ba * block_size, SEEK_SET);
00261         if (rc < 0) {
00262                 fibril_mutex_unlock(&dev_lock);
00263                 return EIO;
00264         }
00265 
00266         n_rd = fread(buf, block_size, cnt, img);
00267 
00268         if (ferror(img)) {
00269                 fibril_mutex_unlock(&dev_lock);
00270                 return EIO;     /* Read error */
00271         }
00272 
00273         fibril_mutex_unlock(&dev_lock);
00274 
00275         if (n_rd < cnt)
00276                 return EINVAL;  /* Read beyond end of device */
00277 
00278         return EOK;
00279 }
00280 
00282 static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
00283 {
00284         size_t n_wr;
00285         int rc;
00286 
00287         /* Check whether access is within device address bounds. */
00288         if (ba + cnt > num_blocks) {
00289                 printf(NAME ": Accessed blocks %" PRIuOFF64 "-%" PRIuOFF64 ", while "
00290                     "max block number is %" PRIuOFF64 ".\n", ba, ba + cnt - 1,
00291                     num_blocks - 1);
00292                 return ELIMIT;
00293         }
00294 
00295         fibril_mutex_lock(&dev_lock);
00296 
00297         clearerr(img);
00298         rc = fseek(img, ba * block_size, SEEK_SET);
00299         if (rc < 0) {
00300                 fibril_mutex_unlock(&dev_lock);
00301                 return EIO;
00302         }
00303 
00304         n_wr = fwrite(buf, block_size, cnt, img);
00305 
00306         if (ferror(img) || n_wr < cnt) {
00307                 fibril_mutex_unlock(&dev_lock);
00308                 return EIO;     /* Write error */
00309         }
00310 
00311         if (fflush(img) != 0) {
00312                 fibril_mutex_unlock(&dev_lock);
00313                 return EIO;
00314         }
00315 
00316         fibril_mutex_unlock(&dev_lock);
00317 
00318         return EOK;
00319 }
00320 

Generated on Thu Jun 2 07:45:49 2011 for HelenOS/USB by  doxygen 1.4.7