aboutsummaryrefslogtreecommitdiff
path: root/remap.c
diff options
context:
space:
mode:
Diffstat (limited to 'remap.c')
-rw-r--r--remap.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/remap.c b/remap.c
new file mode 100644
index 0000000..ab96d04
--- /dev/null
+++ b/remap.c
@@ -0,0 +1,219 @@
+/* Copyright 2021 Alain Knaff.
+ * This file is part of mtools.
+ *
+ * Mtools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mtools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Remapping shim
+ */
+
+#include "sysincludes.h"
+#include "msdos.h"
+#include "mtools.h"
+#include "remap.h"
+
+enum map_type_t {
+ DATA,
+ ZERO,
+ SKIP,
+ POS
+};
+
+struct map {
+ mt_off_t orig;
+ mt_off_t remapped;
+ enum map_type_t type;
+};
+
+typedef struct Remap_t {
+ struct Stream_t head;
+
+ struct map *map;
+ int mapSize;
+
+ mt_off_t net_offset;
+} Remap_t;
+
+static enum map_type_t remap(Remap_t *This, mt_off_t *start, size_t *len) {
+ int i;
+ for(i=0; i < This->mapSize - 1; i++)
+ if(*start < This->map[i+1].remapped) {
+ limitSizeToOffT(len, This->map[i+1].remapped - *start);
+ break;
+ }
+ *start = *start - This->map[i].remapped + This->map[i].orig;
+ return This->map[i].type;
+}
+
+static ssize_t remap_pread(Stream_t *Stream, char *buf,
+ mt_off_t start, size_t len)
+{
+ DeclareThis(Remap_t);
+ if(remap(This, &start, &len)==DATA)
+ return PREADS(This->head.Next, buf, start, len);
+ else {
+ memset(buf, 0, len);
+ return (ssize_t) len;
+ }
+}
+
+static ssize_t remap_pwrite(Stream_t *Stream, char *buf,
+ mt_off_t start, size_t len)
+{
+ DeclareThis(Remap_t);
+ if(remap(This, &start, &len)==DATA)
+ return PWRITES(This->head.Next, buf, start, len);
+ else {
+ unsigned int i;
+ /* When writing to a "zero" sector, make sure that we
+ indeed only write zeroes back to there. Helps catch
+ putting filesystems with parameters unsuitable to
+ the particular mapping */
+ for(i=0; i<len; i++) {
+ if(buf[i]) {
+ fprintf(stderr, "Bad data written to unmapped sectors\n");
+ errno = EFAULT;
+ return -1;
+ }
+ }
+ return (ssize_t) len;
+ }
+}
+
+static int remap_free(Stream_t *Stream)
+{
+ DeclareThis(Remap_t);
+ if(This->map)
+ free(This->map);
+ return 0;
+}
+
+static Class_t RemapClass = {
+ 0,
+ 0,
+ remap_pread,
+ remap_pwrite,
+ 0, /* flush */
+ remap_free, /* free */
+ set_geom_pass_through, /* set_geom */
+ 0, /* get_data */
+ 0, /* pre-allocate */
+ get_dosConvert_pass_through, /* dos convert */
+ 0, /* discard */
+};
+
+static int process_map(Remap_t *This, const char *ptr,
+ int countOnly, char *errmsg) {
+ mt_off_t orig=0;
+ mt_off_t remapped=0;
+ int count=0;
+ int atEnd=0;
+ char *eptr;
+ while(!atEnd) {
+ mt_off_t len;
+ enum map_type_t type;
+ if(*ptr=='\0') {
+ type=DATA;
+ atEnd=1;
+ } else if(!strncmp(ptr, "skip", 4)) {
+ type=SKIP;
+ ptr+=4;
+ } else if(!strncmp(ptr, "zero", 4)) {
+ type=ZERO;
+ ptr+=4;
+ } else if(!strncmp(ptr, "pos", 3)) {
+ type=POS;
+ ptr+=3;
+ } else {
+ type=DATA;
+ }
+
+ len=str_to_off_with_end(ptr,&eptr);
+ ptr=eptr;
+ switch(*ptr) {
+ case '\0':
+ /* End of string */
+ break;
+ case ',':
+ /* Move on to next item */
+ ptr++;
+ break;
+ default:
+ sprintf(errmsg, "Bad number %s\n", ptr);
+ return -1;
+ }
+
+ if(type == POS) {
+ orig = len;
+ continue;
+ }
+ if(type != SKIP) {
+ if(!countOnly) {
+ struct map *m = This->map+count;
+ m->orig = orig;
+ m->remapped = remapped;
+ m->type = type;
+ }
+ remapped+=len;
+ count++;
+ }
+ if(type != ZERO) {
+ orig+=len;
+ }
+
+ }
+ This->net_offset = orig-remapped;
+ return count;
+}
+
+
+Stream_t *Remap(Stream_t *Next, struct device *dev, char *errmsg) {
+ Remap_t *This;
+ int nrItems=0;
+ const char *map = dev->data_map;
+
+ This = New(Remap_t);
+ if (!This){
+ printOom();
+ return 0;
+ }
+ memset((void*)This, 0, sizeof(Remap_t));
+ init_head(&This->head, &RemapClass, Next);
+
+ /* First count number of items */
+ nrItems=process_map(This, map, 1, errmsg);
+ if(nrItems < 0) {
+ free(This);
+ return NULL;
+ }
+
+ This->map = calloc((size_t)nrItems, sizeof(struct map));
+ if(!This->map) {
+ printOom();
+ goto exit_0;
+ }
+
+ process_map(This, map, 0, errmsg);
+
+ if(adjust_tot_sectors(dev, This->net_offset, errmsg) < 0)
+ goto exit_1;
+
+ This->mapSize=nrItems;
+ return &This->head;
+ exit_1:
+ free(This->map);
+ exit_0:
+ free(This);
+ printOom();
+ return NULL;
+}