Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 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 | * Buffer read/write module |
| 18 | */ |
| 19 | |
| 20 | #include "sysincludes.h" |
| 21 | #include "msdos.h" |
| 22 | #include "mtools.h" |
| 23 | #include "partition.h" |
| 24 | |
| 25 | typedef struct Partition_t { |
| 26 | struct Stream_t head; |
| 27 | |
| 28 | mt_off_t offset; /* Offset, in bytes */ |
| 29 | mt_off_t size; /* size, in bytes */ |
| 30 | uint32_t nbSect; /* size, in sectors */ |
| 31 | |
| 32 | uint8_t pos; |
| 33 | |
| 34 | uint8_t sectors; |
| 35 | uint8_t heads; |
| 36 | uint16_t cyclinders; |
| 37 | } Partition_t; |
| 38 | |
| 39 | static __inline__ void print_hsc(hsc *h) |
| 40 | { |
| 41 | printf(" h=%d s=%d c=%d\n", |
| 42 | head(*h), sector(*h), cyl(*h)); |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | * Make sure range [ start, end ] does not overlap with partition i |
| 47 | */ |
| 48 | static int overlapCheck(struct partition *partTable, unsigned int i, |
| 49 | uint32_t start, uint32_t end) { |
| 50 | struct partition *partition = &partTable[i]; |
| 51 | if(!partition->sys_ind) |
| 52 | return 0; /* Partition not allocated => ok */ |
| 53 | if(end > BEGIN(partition) && |
| 54 | (start < END(partition) || END(partition) < BEGIN(partition))) |
| 55 | /* overlap */ |
| 56 | return -1; |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | unsigned int findOverlap(struct partition *partTable, unsigned int until, |
| 61 | uint32_t start, uint32_t end) |
| 62 | { |
| 63 | unsigned int i; |
| 64 | for(i=1; i <= until; i++) |
| 65 | if(overlapCheck(partTable, i, start, end)) |
| 66 | return i; |
| 67 | return 0; |
| 68 | } |
| 69 | |
| 70 | |
| 71 | int consistencyCheck(struct partition *partTable, int doprint, |
| 72 | int verbose, |
| 73 | int *has_activated, uint32_t tot_sectors, |
| 74 | struct device *used_dev UNUSEDP, |
| 75 | unsigned int target_partition) |
| 76 | { |
| 77 | unsigned int i; |
| 78 | bool inconsistency; |
| 79 | |
| 80 | /* quick consistency check */ |
| 81 | inconsistency = 0; |
| 82 | *has_activated = 0; |
| 83 | for(i=1; i<=4; i++){ |
| 84 | unsigned int j; |
| 85 | struct partition *partition = &partTable[i]; |
| 86 | if(!partition->sys_ind) |
| 87 | continue; |
| 88 | if(partition->boot_ind) |
| 89 | (*has_activated)++; |
| 90 | |
| 91 | if(END(partition) < BEGIN(partition)) { |
| 92 | fprintf(stderr, |
| 93 | "End of partition %d before its begin\n", |
| 94 | i); |
| 95 | } |
| 96 | |
| 97 | if((j = findOverlap(partTable, i-1, |
| 98 | BEGIN(partition), END(partition)))) { |
| 99 | fprintf(stderr, |
| 100 | "Partitions %d and %d overlap\n", |
| 101 | j, i); |
| 102 | inconsistency=1; |
| 103 | } |
| 104 | |
| 105 | if(tot_sectors && END(partition) >tot_sectors) { |
| 106 | fprintf(stderr, |
| 107 | "Partition %d extends beyond end of disk\n", i); |
| 108 | } |
| 109 | |
| 110 | if(doprint && verbose) { |
| 111 | if(i==target_partition) |
| 112 | putchar('*'); |
| 113 | else |
| 114 | putchar(' '); |
| 115 | printf("Partition %d\n",i); |
| 116 | |
| 117 | printf(" active=%x\n", partition->boot_ind); |
| 118 | printf(" start:"); |
| 119 | print_hsc(&partition->start); |
| 120 | printf(" type=0x%x\n", partition->sys_ind); |
| 121 | printf(" end:"); |
| 122 | print_hsc(&partition->end); |
| 123 | printf(" start=%d\n", BEGIN(partition)); |
| 124 | printf(" nr=%d\n", _DWORD(partition->nr_sects)); |
| 125 | printf("\n"); |
| 126 | } |
| 127 | } |
| 128 | return inconsistency; |
| 129 | } |
| 130 | |
| 131 | |
| 132 | static int limit_size(Partition_t *This, mt_off_t start, size_t *len) |
| 133 | { |
| 134 | if(start > This->size) |
| 135 | return -1; |
| 136 | limitSizeToOffT(len, This->size - start); |
| 137 | return 0; |
| 138 | } |
| 139 | |
| 140 | static ssize_t partition_pread(Stream_t *Stream, char *buf, |
| 141 | mt_off_t start, size_t len) |
| 142 | { |
| 143 | DeclareThis(Partition_t); |
| 144 | if(limit_size(This, start, &len) < 0) |
| 145 | return -1; |
| 146 | return PREADS(This->head.Next, buf, start+This->offset, len); |
| 147 | } |
| 148 | |
| 149 | static ssize_t partition_pwrite(Stream_t *Stream, char *buf, |
| 150 | mt_off_t start, size_t len) |
| 151 | { |
| 152 | DeclareThis(Partition_t); |
| 153 | if(limit_size(This, start, &len) < 0) |
| 154 | return -1; |
| 155 | return PWRITES(This->head.Next, buf, start+This->offset, len); |
| 156 | } |
| 157 | |
| 158 | static int partition_data(Stream_t *Stream, time_t *date, mt_off_t *size, |
| 159 | int *type, uint32_t *address) |
| 160 | { |
| 161 | DeclareThis(Partition_t); |
| 162 | |
| 163 | if(date || type || address) { |
| 164 | int ret = GET_DATA(This->head.Next, date, NULL, type, address); |
| 165 | if(ret < 0) |
| 166 | return ret; |
| 167 | } |
| 168 | if(size) |
| 169 | *size = This->size * 512; |
| 170 | return 0; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | static int partition_geom(Stream_t *Stream, struct device *dev, |
| 175 | UNUSEDP struct device *orig_dev) |
| 176 | { |
| 177 | DeclareThis(Partition_t); |
| 178 | |
| 179 | if(!dev->tot_sectors) |
| 180 | dev->tot_sectors = This->nbSect; |
| 181 | |
| 182 | return 0; |
| 183 | } |
| 184 | |
| 185 | static Class_t PartitionClass = { |
| 186 | 0, |
| 187 | 0, |
| 188 | partition_pread, |
| 189 | partition_pwrite, |
| 190 | 0, /* flush */ |
| 191 | 0, /* free */ |
| 192 | partition_geom, /* set_geom */ |
| 193 | partition_data, /* get_data */ |
| 194 | 0, /* pre-allocate */ |
| 195 | get_dosConvert_pass_through, /* dos convert */ |
| 196 | 0, /* discard */ |
| 197 | }; |
| 198 | |
| 199 | Stream_t *OpenPartition(Stream_t *Next, struct device *dev, |
| 200 | char *errmsg, mt_off_t *maxSize) { |
| 201 | Partition_t *This; |
| 202 | int has_activated; |
| 203 | unsigned char buf[2048]; |
| 204 | struct partition *partTable=(struct partition *)(buf+ 0x1ae); |
| 205 | uint32_t partOff; |
| 206 | struct partition *partition; |
| 207 | |
| 208 | if(!dev || (dev->partition > 4) || (dev->partition <= 0)) { |
| 209 | fprintf(stderr, |
| 210 | "Invalid partition %d (must be between 1 and 4), ignoring it\n", |
| 211 | dev->partition); |
| 212 | return NULL; |
| 213 | } |
| 214 | |
| 215 | This = New(Partition_t); |
| 216 | if (!This){ |
| 217 | printOom(); |
| 218 | return 0; |
| 219 | } |
| 220 | memset((void*)This, 0, sizeof(Partition_t)); |
| 221 | init_head(&This->head, &PartitionClass, Next); |
| 222 | |
| 223 | |
| 224 | /* read the first sector, or part of it */ |
| 225 | if (force_pread(This->head.Next, (char*) buf, 0, 512) != 512) |
| 226 | goto exit_0; |
| 227 | if( _WORD(buf+510) != 0xaa55) { |
| 228 | /* Not a partition table */ |
| 229 | if(errmsg) |
| 230 | sprintf(errmsg, |
| 231 | "Device does not have a BIOS partition table\n"); |
| 232 | goto exit_0; |
| 233 | } |
| 234 | partition = &partTable[dev->partition]; |
| 235 | if(!partition->sys_ind) { |
| 236 | if(errmsg) |
| 237 | sprintf(errmsg, |
| 238 | "Partition %d does not exist\n", |
| 239 | dev->partition); |
| 240 | goto exit_0; |
| 241 | } |
| 242 | |
| 243 | partOff = BEGIN(partition); |
| 244 | if (maxSize) { |
| 245 | if (partOff > (smt_off_t)(*maxSize >> 9)) { |
| 246 | if(errmsg) |
| 247 | sprintf(errmsg,"init: Big disks not supported"); |
| 248 | goto exit_0; |
| 249 | } |
| 250 | *maxSize -= partOff << 9; |
| 251 | maximize(*maxSize, ((mt_off_t)PART_SIZE(partition)) << 9); |
| 252 | } |
| 253 | |
| 254 | This->offset = (mt_off_t) partOff << 9; |
| 255 | |
| 256 | if(!mtools_skip_check && |
| 257 | consistencyCheck((struct partition *)(buf+0x1ae), 0, 0, |
| 258 | &has_activated, dev->tot_sectors, dev, 0)) { |
| 259 | fprintf(stderr, |
| 260 | "Warning: inconsistent partition table\n"); |
| 261 | fprintf(stderr, |
| 262 | "Possibly unpartitioned device\n"); |
| 263 | fprintf(stderr, |
| 264 | "\n*** Maybe try without partition=%d in " |
| 265 | "device definition ***\n\n", |
| 266 | dev->partition); |
| 267 | fprintf(stderr, |
| 268 | "If this is a PCMCIA card, or a disk " |
| 269 | "partitioned on another computer, this " |
| 270 | "message may be in error: add " |
| 271 | "mtools_skip_check=1 to your .mtoolsrc " |
| 272 | "file to suppress this warning\n"); |
| 273 | } |
| 274 | dev->tot_sectors = This->nbSect = PART_SIZE(partition); |
| 275 | This->size = (mt_off_t) This->nbSect << 9; |
| 276 | return &This->head; |
| 277 | exit_0: |
| 278 | Free(This); |
| 279 | return NULL; |
| 280 | } |
| 281 | |