xref: /aosp_15_r20/external/mtools/remap.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
1 /*  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 
25 enum map_type_t {
26 		 DATA,
27 		 ZERO,
28 		 SKIP,
29 		 POS
30 };
31 
32 struct map {
33 	mt_off_t orig;
34 	mt_off_t remapped;
35 	enum map_type_t type;
36 };
37 
38 typedef 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 
remap(Remap_t * This,mt_off_t * start,size_t * len)47 static 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 
remap_pread(Stream_t * Stream,char * buf,mt_off_t start,size_t len)58 static 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 
remap_pwrite(Stream_t * Stream,char * buf,mt_off_t start,size_t len)70 static 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 
remap_free(Stream_t * Stream)93 static int remap_free(Stream_t *Stream)
94 {
95 	DeclareThis(Remap_t);
96 	if(This->map)
97 		free(This->map);
98 	return 0;
99 }
100 
101 static 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 
process_map(Remap_t * This,const char * ptr,int countOnly,char * errmsg)115 static 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 
Remap(Stream_t * Next,struct device * dev,char * errmsg)180 Stream_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 }
220