1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * 2017 by Marek Behún <kabel@kernel.org>
4 *
5 * Derived from code in ext4/dev.c, which was based on reiserfs/dev.c
6 */
7
8 #define LOG_CATEGORY LOGC_CORE
9
10 #include <blk.h>
11 #include <compiler.h>
12 #include <log.h>
13 #include <part.h>
14 #include <memalign.h>
15
fs_devread(struct blk_desc * blk,struct disk_partition * partition,lbaint_t sector,int byte_offset,int byte_len,char * buf)16 int fs_devread(struct blk_desc *blk, struct disk_partition *partition,
17 lbaint_t sector, int byte_offset, int byte_len, char *buf)
18 {
19 unsigned block_len;
20 int log2blksz;
21 ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, (blk ? blk->blksz : 0));
22 if (blk == NULL) {
23 log_err("** Invalid Block Device Descriptor (NULL)\n");
24 return 0;
25 }
26 log2blksz = blk->log2blksz;
27
28 /* Check partition boundaries */
29 if ((sector + ((byte_offset + byte_len - 1) >> log2blksz))
30 >= partition->size) {
31 log_err("** Read outside partition " LBAFU "\n", sector);
32 return 0;
33 }
34
35 /* Get the read to the beginning of a partition */
36 sector += byte_offset >> log2blksz;
37 byte_offset &= blk->blksz - 1;
38
39 log_debug(" <" LBAFU ", %d, %d>\n", sector, byte_offset, byte_len);
40
41 if (byte_offset != 0) {
42 int readlen;
43 /* read first part which isn't aligned with start of sector */
44 if (blk_dread(blk, partition->start + sector, 1,
45 (void *)sec_buf) != 1) {
46 log_err(" ** %s read error **\n", __func__);
47 return 0;
48 }
49 readlen = min((int)blk->blksz - byte_offset,
50 byte_len);
51 memcpy(buf, sec_buf + byte_offset, readlen);
52 buf += readlen;
53 byte_len -= readlen;
54 sector++;
55 }
56
57 if (byte_len == 0)
58 return 1;
59
60 /* read sector aligned part */
61 block_len = byte_len & ~(blk->blksz - 1);
62
63 if (block_len == 0) {
64 ALLOC_CACHE_ALIGN_BUFFER(u8, p, blk->blksz);
65
66 block_len = blk->blksz;
67 blk_dread(blk, partition->start + sector, 1,
68 (void *)p);
69 memcpy(buf, p, byte_len);
70 return 1;
71 }
72
73 if (blk_dread(blk, partition->start + sector,
74 block_len >> log2blksz, (void *)buf) !=
75 block_len >> log2blksz) {
76 log_err(" ** %s read error - block\n", __func__);
77 return 0;
78 }
79 block_len = byte_len & ~(blk->blksz - 1);
80 buf += block_len;
81 byte_len -= block_len;
82 sector += block_len / blk->blksz;
83
84 if (byte_len != 0) {
85 /* read rest of data which are not in whole sector */
86 if (blk_dread(blk, partition->start + sector, 1,
87 (void *)sec_buf) != 1) {
88 log_err("* %s read error - last part\n", __func__);
89 return 0;
90 }
91 memcpy(buf, sec_buf, byte_len);
92 }
93 return 1;
94 }
95
fs_devwrite(struct blk_desc * blk,struct disk_partition * partition,lbaint_t sector,int byte_offset,int byte_len,const char * buf)96 int fs_devwrite(struct blk_desc *blk, struct disk_partition *partition,
97 lbaint_t sector, int byte_offset, int byte_len, const char *buf)
98 {
99 unsigned block_len;
100 int log2blksz;
101 ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, (blk ? blk->blksz : 0));
102 if (blk == NULL) {
103 log_err("** Invalid Block Device Descriptor (NULL)\n");
104 return 0;
105 }
106 log2blksz = blk->log2blksz;
107
108 /* Check partition boundaries */
109 if ((sector + ((byte_offset + byte_len - 1) >> log2blksz))
110 >= partition->size) {
111 log_debug("read outside partition " LBAFU "\n", sector);
112 return 0;
113 }
114
115 /* Get the read to the beginning of a partition */
116 sector += byte_offset >> log2blksz;
117 byte_offset &= blk->blksz - 1;
118
119 log_debug(" <" LBAFU ", %d, %d>\n", sector, byte_offset, byte_len);
120
121 if (byte_offset != 0) {
122 int readlen;
123 /* read first part which isn't aligned with start of sector */
124 if (blk_dread(blk, partition->start + sector, 1,
125 (void *)sec_buf) != 1) {
126 log_err(" ** %s read error **\n", __func__);
127 return 0;
128 }
129
130 readlen = min((int)blk->blksz - byte_offset,
131 byte_len);
132 memcpy(sec_buf + byte_offset, buf, readlen);
133
134 if (blk_dwrite(blk, partition->start + sector, 1,
135 (void *)sec_buf) != 1) {
136 log_err(" ** %s write error **\n", __func__);
137 return 0;
138 }
139 buf += readlen;
140 byte_len -= readlen;
141 sector++;
142 }
143
144 if (byte_len == 0)
145 return 1;
146
147 /* write sector aligned part */
148 block_len = byte_len & ~(blk->blksz - 1);
149
150 if (block_len == 0) {
151 if (blk_dread(blk, partition->start + sector, 1,
152 (void *)sec_buf) != 1) {
153 log_err(" ** %s read error **\n", __func__);
154 return 0;
155 }
156
157 memcpy(sec_buf, buf, byte_len);
158
159 if (blk_dwrite(blk, partition->start + sector, 1,
160 (void *)sec_buf) != 1) {
161 log_err(" ** %s write error **\n", __func__);
162 return 0;
163 }
164
165 return 1;
166 }
167
168 if (blk_dwrite(blk, partition->start + sector,
169 block_len >> log2blksz, (void *)buf) !=
170 block_len >> log2blksz) {
171 log_err(" ** %s write error - block\n", __func__);
172 return 0;
173 }
174 block_len = byte_len & ~(blk->blksz - 1);
175 buf += block_len;
176 byte_len -= block_len;
177 sector += block_len / blk->blksz;
178
179 if (byte_len != 0) {
180 /* read rest of data which are not in whole sector */
181 if (blk_dread(blk, partition->start + sector, 1,
182 (void *)sec_buf) != 1) {
183 log_err("* %s read error - last part **\n", __func__);
184 return 0;
185 }
186
187 memcpy(sec_buf, buf, byte_len);
188
189 if (blk_dwrite(blk, partition->start + sector, 1,
190 (void *)sec_buf) != 1) {
191 log_err(" ** %s write error - last part **\n", __func__);
192 return 0;
193 }
194 }
195 return 1;
196 }
197