-
Notifications
You must be signed in to change notification settings - Fork 110
/
disk.c
147 lines (113 loc) · 3.73 KB
/
disk.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
* Copyright (c) 2010, Gerard Lledó Vives, [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
*/
#define _XOPEN_SOURCE 500
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <errno.h>
#include "disk.h"
#include "logging.h"
#ifdef __FreeBSD__
#include <string.h>
#endif
static int disk_fd = -1;
static int pread_wrapper(int disk_fd, void *p, size_t size, off_t where)
{
#if defined(__FreeBSD__) && !defined(__APPLE__)
#define PREAD_BLOCK_SIZE 1024
/* FreeBSD needs to read aligned whole blocks.
* TODO: Check what is a safe block size.
*/
static __thread uint8_t block[PREAD_BLOCK_SIZE];
off_t first_offset = where % PREAD_BLOCK_SIZE;
int ret = 0;
if (first_offset) {
/* This is the case if the read doesn't start on a block boundary.
* We still need to read the whole block and we do, but we only copy to
* the out pointer the bytes that where actually asked for. In this
* case first_offset is the offset into the block. */
int pread_ret = pread(disk_fd, block, PREAD_BLOCK_SIZE, where - first_offset);
ASSERT(pread_ret == PREAD_BLOCK_SIZE);
size_t first_size = MIN(size, (size_t)(PREAD_BLOCK_SIZE - first_offset));
memcpy(p, block + first_offset, first_size);
p += first_size;
size -= first_size;
where += first_size;
ret += first_size;
if (!size) return ret;
}
ASSERT(where % PREAD_BLOCK_SIZE == 0);
size_t mid_read_size = (size / PREAD_BLOCK_SIZE) * PREAD_BLOCK_SIZE;
if (mid_read_size) {
int pread_ret_mid = pread(disk_fd, p, mid_read_size, where);
ASSERT((size_t)pread_ret_mid == mid_read_size);
p += mid_read_size;
size -= mid_read_size;
where += mid_read_size;
ret += mid_read_size;
if (!size) return ret;
}
ASSERT(size < PREAD_BLOCK_SIZE);
int pread_ret_last = pread(disk_fd, block, PREAD_BLOCK_SIZE, where);
ASSERT(pread_ret_last == PREAD_BLOCK_SIZE);
memcpy(p, block, size);
return ret + size;
#else
return pread(disk_fd, p, size, where);
#endif
}
int disk_open(const char *path)
{
disk_fd = open(path, O_RDONLY);
if (disk_fd < 0) {
return -errno;
}
return 0;
}
int __disk_read(off_t where, size_t size, void *p, const char *func, int line)
{
static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
ssize_t pread_ret;
ASSERT(disk_fd >= 0);
pthread_mutex_lock(&read_lock);
DEBUG("Disk Read: 0x%jx +0x%zx [%s:%d]", where, size, func, line);
pread_ret = pread_wrapper(disk_fd, p, size, where);
pthread_mutex_unlock(&read_lock);
if (size == 0) WARNING("Read operation with 0 size");
ASSERT((size_t)pread_ret == size);
return pread_ret;
}
int disk_ctx_create(struct disk_ctx *ctx, off_t where, size_t size, uint32_t len)
{
ASSERT(ctx); /* Should be user allocated */
ASSERT(size);
ctx->cur = where;
ctx->size = size * len;
DEBUG("New disk context: 0x%jx +0x%jx", ctx->cur, ctx->size);
return 0;
}
int __disk_ctx_read(struct disk_ctx *ctx, size_t size, void *p, const char *func, int line)
{
int ret = 0;
ASSERT(ctx->size);
if (ctx->size == 0) {
WARNING("Using a context with no bytes left");
return ret;
}
/* Truncate if there are too many bytes requested */
if (size > ctx->size) {
size = ctx->size;
}
ret = __disk_read(ctx->cur, size, p, func, line);
ctx->size -= ret;
ctx->cur += ret;
return ret;
}