source: mainline/uspace/srv/bd/part/guid_part/guid_part.c@ e70edd1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e70edd1 was ed903174, checked in by Martin Decky <martin@…>, 16 years ago

implement support for 64bit file offsets

  • the libc API is a small deviation from standard, but we have no reason to keep a strict backward compatibility with ancient code so far
    • the basic signed 64bit offset type is called off64_t
      • lseek() and fseek() take off64_t arguments (since the argument represents a relative offset)
      • ftell() returns off64_t values (since it is a wrapper of lseek())
      • vfs_seek() implementation supports negative offsets when SEEK_CUR and SEEK_END is used
    • aoff64_t is used for internal unsigned representation of sizes (in bytes, blocks, etc.) and absolute offsets
      • mmap() and ftruncate() take aoff64_t arguments (since the full range of the absolute file offset should be used here)
      • struct stat stores the file size as aoff64_t
    • in both cases the explicit range of the types shown in the names is helpful for proper filesystem and IPC interaction
    • note: size_t should be used only for representing in-memory sizes and offsets, not device and file-related information, and vice versa
      • the code base still needs a thorough revision with respect to this rule
    • PRIdOFF64 and PRIuOFF64 can be used for printing the offsets
  • VFS_OUT_LOOKUP returns the 64bit file size in two IPC arguments
    • since all 5 IPC arguments have already been taken, the fs_handle is returned as the return value (fs_handle has only 16 bits, thus the return value can be used for both indicating errors as negative values and returning positive handles)
  • VFS_OUT_READ and VFS_OUT_WRITE use aoff64_t absolute offsets split into two IPC arguments

replace bn_t with aoff64_t as a generic 64bit bytes/block counter type

note: filesystem drivers need to be revised with respect to make sure that all out-of-range checks are correct (especially w.r.t. file and block offsets)

  • Property mode set to 100644
File size: 9.9 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bd
30 * @{
31 */
32
33/**
34 * @file
35 * @brief GUID partition table driver
36 *
37 * Handles the GUID partitioning scheme. Uses a block device
38 * and provides one for each partition.
39 *
40 * References:
41 * UEFI Specification Version 2.3, Chapter 5 GUID Partition Table (GPT)
42 * Format, http://www.uefi.org/specs/
43 *
44 */
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <ipc/ipc.h>
50#include <ipc/bd.h>
51#include <async.h>
52#include <as.h>
53#include <fibril_synch.h>
54#include <devmap.h>
55#include <sys/types.h>
56#include <sys/typefmt.h>
57#include <inttypes.h>
58#include <libblock.h>
59#include <devmap.h>
60#include <errno.h>
61#include <bool.h>
62#include <byteorder.h>
63#include <assert.h>
64#include <macros.h>
65#include <task.h>
66
67#include "gpt.h"
68
69#define NAME "guid_part"
70
71const uint8_t efi_signature[8] = {
72 /* "EFI PART" in ASCII */
73 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
74};
75
76/** Partition */
77typedef struct part {
78 /** Partition entry is in use */
79 bool present;
80 /** Address of first block */
81 aoff64_t start_addr;
82 /** Number of blocks */
83 aoff64_t length;
84 /** Device representing the partition (outbound device) */
85 dev_handle_t dev;
86 /** Points to next partition structure. */
87 struct part *next;
88} part_t;
89
90static size_t block_size;
91
92/** Partitioned device (inbound device) */
93static dev_handle_t indev_handle;
94
95/** List of partitions. This structure is an empty head. */
96static part_t plist_head;
97
98static int gpt_init(const char *dev_name);
99static int gpt_read(void);
100static part_t *gpt_part_new(void);
101static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
102static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall);
103static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf);
104static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf);
105static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
106
107int main(int argc, char **argv)
108{
109 printf(NAME ": GUID partition table driver\n");
110
111 if (argc != 2) {
112 printf("Expected one argument (device name).\n");
113 return -1;
114 }
115
116 if (gpt_init(argv[1]) != EOK)
117 return -1;
118
119 printf(NAME ": Accepting connections\n");
120 task_retval(0);
121 async_manager();
122
123 /* Not reached */
124 return 0;
125}
126
127static int gpt_init(const char *dev_name)
128{
129 int rc;
130 int i;
131 char *name;
132 dev_handle_t dev;
133 uint64_t size_mb;
134 part_t *part;
135
136 rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
137 if (rc != EOK) {
138 printf(NAME ": could not resolve device `%s'.\n", dev_name);
139 return rc;
140 }
141
142 rc = block_init(indev_handle, 2048);
143 if (rc != EOK) {
144 printf(NAME ": could not init libblock.\n");
145 return rc;
146 }
147
148 /* Determine and verify block size. */
149
150 rc = block_get_bsize(indev_handle, &block_size);
151 if (rc != EOK) {
152 printf(NAME ": error getting block size.\n");
153 return rc;
154 }
155
156 if (block_size < 512 || (block_size % 512) != 0) {
157 printf(NAME ": invalid block size %d.\n");
158 return ENOTSUP;
159 }
160
161 /* Read in partition records. */
162 rc = gpt_read();
163 if (rc != EOK)
164 return rc;
165
166 /* Register the driver with device mapper. */
167 rc = devmap_driver_register(NAME, gpt_connection);
168 if (rc != EOK) {
169 printf(NAME ": Unable to register driver.\n");
170 return rc;
171 }
172
173 /*
174 * Create partition devices.
175 */
176 i = 0;
177 part = plist_head.next;
178
179 while (part != NULL) {
180 /* Skip absent partitions. */
181 if (!part->present) {
182 part = part->next;
183 ++i;
184 continue;
185 }
186
187 asprintf(&name, "%sp%d", dev_name, i);
188 if (name == NULL)
189 return ENOMEM;
190
191 rc = devmap_device_register(name, &dev);
192 if (rc != EOK) {
193 devmap_hangup_phone(DEVMAP_DRIVER);
194 printf(NAME ": Unable to register device %s.\n", name);
195 return rc;
196 }
197
198 size_mb = (part->length * block_size + 1024 * 1024 - 1)
199 / (1024 * 1024);
200 printf(NAME ": Registered device %s: %" PRIu64 " blocks "
201 "%" PRIuOFF64 " MB.\n", name, part->length, size_mb);
202
203 part->dev = dev;
204 free(name);
205
206 part = part->next;
207 ++i;
208 }
209
210 return EOK;
211}
212
213/** Read in partition records. */
214static int gpt_read(void)
215{
216 int i, rc;
217 gpt_header_t *gpt_hdr;
218 gpt_entry_t *etable;
219 uint64_t ba;
220 uint32_t bcnt;
221 uint32_t esize;
222 uint32_t num_entries;
223 uint32_t entry;
224 part_t *prev, *p;
225
226 gpt_hdr = malloc(block_size);
227 if (gpt_hdr == NULL) {
228 printf(NAME ": Failed allocating memory.\n");
229 return ENOMEM;
230 }
231
232 rc = block_read_direct(indev_handle, GPT_HDR_BA, 1, gpt_hdr);
233 if (rc != EOK) {
234 printf(NAME ": Failed reading GPT header block.\n");
235 return rc;
236 }
237
238 for (i = 0; i < 8; ++i) {
239 if (gpt_hdr->efi_signature[i] != efi_signature[i]) {
240 printf(NAME ": Invalid GPT signature.\n");
241 return EINVAL;
242 }
243 }
244
245 plist_head.next = NULL;
246 prev = &plist_head;
247
248 num_entries = uint32_t_le2host(gpt_hdr->num_entries);
249 ba = uint64_t_le2host(gpt_hdr->entry_lba);
250 esize = uint32_t_le2host(gpt_hdr->entry_size);
251 bcnt = (num_entries / esize) + ((num_entries % esize != 0) ? 1 : 0);
252
253 etable = malloc(num_entries * esize);
254 if (etable == NULL) {
255 free(gpt_hdr);
256 printf(NAME ": Failed allocating memory.\n");
257 return ENOMEM;
258 }
259
260 rc = block_read_direct(indev_handle, ba, bcnt, etable);
261 if (rc != EOK) {
262 printf(NAME ": Failed reading GPT entries.\n");
263 return rc;
264 }
265
266 for (entry = 0; entry < num_entries; ++entry) {
267 p = gpt_part_new();
268 if (p == NULL)
269 return ENOMEM;
270
271 gpt_pte_to_part(&etable[entry], p);
272 prev->next = p;
273 prev = p;
274 }
275
276 free(etable);
277
278 return EOK;
279}
280
281/** Allocate a new @c part_t structure. */
282static part_t *gpt_part_new(void)
283{
284 return malloc(sizeof(part_t));
285}
286
287/** Parse partition table entry. */
288static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part)
289{
290 uint64_t sa, len;
291 int i;
292
293 /* Partition spans addresses [start_lba, end_lba] (inclusive). */
294 sa = uint64_t_le2host(pte->start_lba);
295 len = uint64_t_le2host(pte->end_lba) + 1 - sa;
296
297 part->start_addr = sa;
298 part->length = len;
299
300 part->present = false;
301
302 for (i = 0; i < 8; ++i) {
303 if (pte->part_type[i] != 0x00)
304 part->present = true;
305 }
306
307 part->dev = 0;
308 part->next = NULL;
309}
310
311static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall)
312{
313 size_t comm_size;
314 void *fs_va = NULL;
315 ipc_callid_t callid;
316 ipc_call_t call;
317 ipcarg_t method;
318 dev_handle_t dh;
319 int flags;
320 int retval;
321 aoff64_t ba;
322 size_t cnt;
323 part_t *part;
324
325 /* Get the device handle. */
326 dh = IPC_GET_ARG1(*icall);
327
328 /*
329 * Determine which partition device is the client connecting to.
330 * A linear search is not terribly fast, but we only do this
331 * once for each connection.
332 */
333 part = plist_head.next;
334 while (part != NULL && part->dev != dh)
335 part = part->next;
336
337 if (part == NULL) {
338 ipc_answer_0(iid, EINVAL);
339 return;
340 }
341
342 assert(part->present == true);
343
344 /* Answer the IPC_M_CONNECT_ME_TO call. */
345 ipc_answer_0(iid, EOK);
346
347 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
348 ipc_answer_0(callid, EHANGUP);
349 return;
350 }
351
352 fs_va = as_get_mappable_page(comm_size);
353 if (fs_va == NULL) {
354 ipc_answer_0(callid, EHANGUP);
355 return;
356 }
357
358 (void) async_share_out_finalize(callid, fs_va);
359
360 while (1) {
361 callid = async_get_call(&call);
362 method = IPC_GET_METHOD(call);
363 switch (method) {
364 case IPC_M_PHONE_HUNGUP:
365 /* The other side has hung up. */
366 ipc_answer_0(callid, EOK);
367 return;
368 case BD_READ_BLOCKS:
369 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
370 IPC_GET_ARG2(call));
371 cnt = IPC_GET_ARG3(call);
372 if (cnt * block_size > comm_size) {
373 retval = ELIMIT;
374 break;
375 }
376 retval = gpt_bd_read(part, ba, cnt, fs_va);
377 break;
378 case BD_WRITE_BLOCKS:
379 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
380 IPC_GET_ARG2(call));
381 cnt = IPC_GET_ARG3(call);
382 if (cnt * block_size > comm_size) {
383 retval = ELIMIT;
384 break;
385 }
386 retval = gpt_bd_write(part, ba, cnt, fs_va);
387 break;
388 case BD_GET_BLOCK_SIZE:
389 ipc_answer_1(callid, EOK, block_size);
390 continue;
391 case BD_GET_NUM_BLOCKS:
392 ipc_answer_2(callid, EOK, LOWER32(part->length),
393 UPPER32(part->length));
394 continue;
395 default:
396 retval = EINVAL;
397 break;
398 }
399 ipc_answer_0(callid, retval);
400 }
401}
402
403/** Read blocks from partition. */
404static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf)
405{
406 aoff64_t gba;
407
408 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
409 return ELIMIT;
410
411 return block_read_direct(indev_handle, gba, cnt, buf);
412}
413
414/** Write blocks to partition. */
415static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf)
416{
417 aoff64_t gba;
418
419 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
420 return ELIMIT;
421
422 return block_write_direct(indev_handle, gba, cnt, buf);
423}
424
425/** Translate block segment address with range checking. */
426static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba)
427{
428 if (ba + cnt > p->length)
429 return ELIMIT;
430
431 *gba = p->start_addr + ba;
432 return EOK;
433}
434
435/**
436 * @}
437 */
Note: See TracBrowser for help on using the repository browser.