blob: ab96d04e5c3db3fc764c3dcc01b86f82bc385134 [file] [log] [blame]
Yi Kong39bbd962022-01-09 19:41:38 +08001/* Copyright 2021 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Remapping shim
18 */
19
20#include "sysincludes.h"
21#include "msdos.h"
22#include "mtools.h"
23#include "remap.h"
24
25enum map_type_t {
26 DATA,
27 ZERO,
28 SKIP,
29 POS
30};
31
32struct map {
33 mt_off_t orig;
34 mt_off_t remapped;
35 enum map_type_t type;
36};
37
38typedef struct Remap_t {
39 struct Stream_t head;
40
41 struct map *map;
42 int mapSize;
43
44 mt_off_t net_offset;
45} Remap_t;
46
47static enum map_type_t remap(Remap_t *This, mt_off_t *start, size_t *len) {
48 int i;
49 for(i=0; i < This->mapSize - 1; i++)
50 if(*start < This->map[i+1].remapped) {
51 limitSizeToOffT(len, This->map[i+1].remapped - *start);
52 break;
53 }
54 *start = *start - This->map[i].remapped + This->map[i].orig;
55 return This->map[i].type;
56}
57
58static ssize_t remap_pread(Stream_t *Stream, char *buf,
59 mt_off_t start, size_t len)
60{
61 DeclareThis(Remap_t);
62 if(remap(This, &start, &len)==DATA)
63 return PREADS(This->head.Next, buf, start, len);
64 else {
65 memset(buf, 0, len);
66 return (ssize_t) len;
67 }
68}
69
70static ssize_t remap_pwrite(Stream_t *Stream, char *buf,
71 mt_off_t start, size_t len)
72{
73 DeclareThis(Remap_t);
74 if(remap(This, &start, &len)==DATA)
75 return PWRITES(This->head.Next, buf, start, len);
76 else {
77 unsigned int i;
78 /* When writing to a "zero" sector, make sure that we
79 indeed only write zeroes back to there. Helps catch
80 putting filesystems with parameters unsuitable to
81 the particular mapping */
82 for(i=0; i<len; i++) {
83 if(buf[i]) {
84 fprintf(stderr, "Bad data written to unmapped sectors\n");
85 errno = EFAULT;
86 return -1;
87 }
88 }
89 return (ssize_t) len;
90 }
91}
92
93static int remap_free(Stream_t *Stream)
94{
95 DeclareThis(Remap_t);
96 if(This->map)
97 free(This->map);
98 return 0;
99}
100
101static Class_t RemapClass = {
102 0,
103 0,
104 remap_pread,
105 remap_pwrite,
106 0, /* flush */
107 remap_free, /* free */
108 set_geom_pass_through, /* set_geom */
109 0, /* get_data */
110 0, /* pre-allocate */
111 get_dosConvert_pass_through, /* dos convert */
112 0, /* discard */
113};
114
115static int process_map(Remap_t *This, const char *ptr,
116 int countOnly, char *errmsg) {
117 mt_off_t orig=0;
118 mt_off_t remapped=0;
119 int count=0;
120 int atEnd=0;
121 char *eptr;
122 while(!atEnd) {
123 mt_off_t len;
124 enum map_type_t type;
125 if(*ptr=='\0') {
126 type=DATA;
127 atEnd=1;
128 } else if(!strncmp(ptr, "skip", 4)) {
129 type=SKIP;
130 ptr+=4;
131 } else if(!strncmp(ptr, "zero", 4)) {
132 type=ZERO;
133 ptr+=4;
134 } else if(!strncmp(ptr, "pos", 3)) {
135 type=POS;
136 ptr+=3;
137 } else {
138 type=DATA;
139 }
140
141 len=str_to_off_with_end(ptr,&eptr);
142 ptr=eptr;
143 switch(*ptr) {
144 case '\0':
145 /* End of string */
146 break;
147 case ',':
148 /* Move on to next item */
149 ptr++;
150 break;
151 default:
152 sprintf(errmsg, "Bad number %s\n", ptr);
153 return -1;
154 }
155
156 if(type == POS) {
157 orig = len;
158 continue;
159 }
160 if(type != SKIP) {
161 if(!countOnly) {
162 struct map *m = This->map+count;
163 m->orig = orig;
164 m->remapped = remapped;
165 m->type = type;
166 }
167 remapped+=len;
168 count++;
169 }
170 if(type != ZERO) {
171 orig+=len;
172 }
173
174 }
175 This->net_offset = orig-remapped;
176 return count;
177}
178
179
180Stream_t *Remap(Stream_t *Next, struct device *dev, char *errmsg) {
181 Remap_t *This;
182 int nrItems=0;
183 const char *map = dev->data_map;
184
185 This = New(Remap_t);
186 if (!This){
187 printOom();
188 return 0;
189 }
190 memset((void*)This, 0, sizeof(Remap_t));
191 init_head(&This->head, &RemapClass, Next);
192
193 /* First count number of items */
194 nrItems=process_map(This, map, 1, errmsg);
195 if(nrItems < 0) {
196 free(This);
197 return NULL;
198 }
199
200 This->map = calloc((size_t)nrItems, sizeof(struct map));
201 if(!This->map) {
202 printOom();
203 goto exit_0;
204 }
205
206 process_map(This, map, 0, errmsg);
207
208 if(adjust_tot_sectors(dev, This->net_offset, errmsg) < 0)
209 goto exit_1;
210
211 This->mapSize=nrItems;
212 return &This->head;
213 exit_1:
214 free(This->map);
215 exit_0:
216 free(This);
217 printOom();
218 return NULL;
219}