00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
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
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
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
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
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;
00271 }
00272
00273 fibril_mutex_unlock(&dev_lock);
00274
00275 if (n_rd < cnt)
00276 return EINVAL;
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
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;
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