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