-
Notifications
You must be signed in to change notification settings - Fork 110
/
op_read.c
119 lines (95 loc) · 3.64 KB
/
op_read.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
/*
* 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.
*/
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <inttypes.h>
#include "common.h"
#include "disk.h"
#include "inode.h"
#include "logging.h"
#include "ops.h"
/* We truncate the read size if it exceeds the limits of the file. */
static size_t truncate_size(struct ext4_inode *inode, size_t size, size_t offset)
{
uint64_t inode_size = inode_get_size(inode);
DEBUG("Truncate? %zd/%d", offset + size, inode_size);
if (offset >= inode_size) {
return 0;
}
if ((offset + size) >= inode_size) {
DEBUG("Truncating read(2) to %"PRIu64" bytes", inode_size);
return inode_size - offset;
}
return size;
}
/* This function reads all necessary data until the offset is aligned */
static size_t first_read(struct ext4_inode *inode, char *buf, size_t size, off_t offset)
{
/* Reason for the -1 is that offset = 0 and size = BLOCK_SIZE is all on the
* same block. Meaning that byte at offset + size is not actually read. */
uint32_t end_lblock = (offset + (size - 1)) / BLOCK_SIZE;
uint32_t start_lblock = offset / BLOCK_SIZE;
uint32_t start_block_off = offset % BLOCK_SIZE;
/* If the size is zero, or we are already aligned, skip over this */
if (size == 0) return 0;
if (start_block_off == 0) return 0;
uint64_t start_pblock = inode_get_data_pblock(inode, start_lblock, NULL);
/* Check if all the read request lays on the same block */
if (start_lblock == end_lblock) {
disk_read(BLOCKS2BYTES(start_pblock) + start_block_off, size, buf);
return size;
} else {
size_t size_to_block_end = ALIGN_TO_BLOCKSIZE(offset) - offset;
ASSERT((offset + size_to_block_end) % BLOCK_SIZE == 0);
disk_read(BLOCKS2BYTES(start_pblock) + start_block_off, size_to_block_end, buf);
return size_to_block_end;
}
}
int op_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t un_offset = (size_t)offset;
struct ext4_inode inode;
size_t ret = 0;
uint32_t extent_len;
/* Not sure if this is possible at all... */
ASSERT(offset >= 0);
DEBUG("read(%s, buf, %zd, %zd, fi->fh=%d)", path, size, un_offset, fi->fh);
int inode_get_ret = inode_get_by_number(fi->fh, &inode);
if (inode_get_ret < 0) {
return inode_get_ret;
}
size = truncate_size(&inode, size, un_offset);
ret = first_read(&inode, buf, size, un_offset);
buf += ret;
un_offset += ret;
for (unsigned int lblock = un_offset / BLOCK_SIZE; size > ret; lblock += extent_len) {
uint64_t pblock = inode_get_data_pblock(&inode, lblock, &extent_len);
size_t bytes;
if (pblock) {
struct disk_ctx read_ctx;
disk_ctx_create(&read_ctx, BLOCKS2BYTES(pblock), BLOCK_SIZE, extent_len);
bytes = disk_ctx_read(&read_ctx, size - ret, buf);
} else {
bytes = size - ret;
if (bytes > BLOCK_SIZE) {
bytes = BLOCK_SIZE;
}
memset(buf,0,bytes);
DEBUG("sparse file, skipping %d bytes",bytes);
}
ret += bytes;
buf += bytes;
DEBUG("Read %zd/%zd bytes from %d consecutive blocks", ret, size, extent_len);
}
/* We always read as many bytes as requested (after initial truncation) */
ASSERT(size == ret);
return ret;
}