| 109 | | static void rdcf_get_date_and_time(struct rdcf_date_and_time *p) |
|---|
| 110 | | { |
|---|
| 111 | | struct tm dateTime; |
|---|
| 112 | | struct timeval temp; |
|---|
| 113 | | time_t now; |
|---|
| 114 | | |
|---|
| 115 | | gettimeofday(&temp, 0); |
|---|
| 116 | | now = temp.tv_sec; |
|---|
| 117 | | localtime_r(&now, &dateTime); |
|---|
| 118 | | // time is correct. |
|---|
| 119 | | p->hour = dateTime.tm_hour; |
|---|
| 120 | | p->minute = dateTime.tm_min; |
|---|
| 121 | | p->second = dateTime.tm_sec; |
|---|
| 122 | | p->month = dateTime.tm_mon+1; |
|---|
| 123 | | p->day = dateTime.tm_mday; |
|---|
| 124 | | p->year = dateTime.tm_year+1900; |
|---|
| | 109 | static void rdcf_get_date_and_time (struct rdcf_date_and_time *p) |
|---|
| | 110 | { |
|---|
| | 111 | struct tm dateTime; |
|---|
| | 112 | struct timeval temp; |
|---|
| | 113 | time_t now; |
|---|
| | 114 | |
|---|
| | 115 | gettimeofday (&temp, 0); |
|---|
| | 116 | now = temp.tv_sec; |
|---|
| | 117 | localtime_r (&now, &dateTime); |
|---|
| | 118 | // time is correct. |
|---|
| | 119 | p->hour = dateTime.tm_hour; |
|---|
| | 120 | p->minute = dateTime.tm_min; |
|---|
| | 121 | p->second = dateTime.tm_sec; |
|---|
| | 122 | p->month = dateTime.tm_mon + 1; |
|---|
| | 123 | p->day = dateTime.tm_mday; |
|---|
| | 124 | p->year = dateTime.tm_year + 1900; |
|---|
| 157 | | static void flush_buffer(struct rdcf *f) |
|---|
| 158 | | { |
|---|
| 159 | | if (f->buffer_status == DIRTY) { |
|---|
| 160 | | // here is where we keep from thrashing while doing FAT operations. |
|---|
| 161 | | // if the sector to be written is in the first FAT table, then |
|---|
| 162 | | // mirror the write to corresponding sector in second FAT. |
|---|
| 163 | | write_sector(f, f->sector_in_buffer, f->buffer.buf); |
|---|
| 164 | | if ((f->sector_in_buffer >= f->first_FAT_sector) && |
|---|
| 165 | | (f->sector_in_buffer <= f->first_FAT_sector+f->sectors_per_FAT)) { |
|---|
| 166 | | // mirror. |
|---|
| 167 | | write_sector(f, f->sector_in_buffer+f->sectors_per_FAT, f->buffer.buf); |
|---|
| 168 | | } |
|---|
| 169 | | f->buffer_status = CLEAN; |
|---|
| 170 | | } |
|---|
| | 160 | static void flush_buffer (struct rdcf *f) |
|---|
| | 161 | { |
|---|
| | 162 | if (f->buffer_status == DIRTY) { |
|---|
| | 163 | // here is where we keep from thrashing while doing FAT operations. |
|---|
| | 164 | // if the sector to be written is in the first FAT table, then |
|---|
| | 165 | // mirror the write to corresponding sector in second FAT. |
|---|
| | 166 | write_sector (f, f->sector_in_buffer, f->buffer.buf); |
|---|
| | 167 | if ((f->sector_in_buffer >= f->first_FAT_sector) && |
|---|
| | 168 | (f->sector_in_buffer <= f->first_FAT_sector + f->sectors_per_FAT)) { |
|---|
| | 169 | // mirror. |
|---|
| | 170 | write_sector (f, f->sector_in_buffer + f->sectors_per_FAT, |
|---|
| | 171 | f->buffer.buf); |
|---|
| | 172 | } |
|---|
| | 173 | f->buffer_status = CLEAN; |
|---|
| | 174 | } |
|---|
| 204 | | static unsigned FAT_entry(struct rdcf *f, unsigned cluster) |
|---|
| 205 | | { |
|---|
| 206 | | check_cluster(f, cluster); |
|---|
| 207 | | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
|---|
| 208 | | unsigned byte_index = cluster + (cluster>>1); |
|---|
| 209 | | uint8_t p[2]; |
|---|
| 210 | | read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE); |
|---|
| 211 | | p[0] = f->buffer.buf[byte_index%SECTOR_SIZE]; |
|---|
| 212 | | byte_index++; |
|---|
| 213 | | read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE); |
|---|
| 214 | | p[1] = f->buffer.buf[byte_index%SECTOR_SIZE]; |
|---|
| 215 | | return (cluster&1) ? (p[1]<<4 | p[0]>>4) : (p[0] | p[1])<<8&0xF00; |
|---|
| 216 | | } else { |
|---|
| 217 | | ushort x; |
|---|
| 218 | | read_buffer(f, f->first_FAT_sector + cluster/(SECTOR_SIZE/2)); |
|---|
| 219 | | x = f->buffer.fat[cluster%(SECTOR_SIZE/2)]; |
|---|
| 220 | | #ifdef _BIG_ENDIAN |
|---|
| 221 | | convert_short(x); |
|---|
| 222 | | #endif |
|---|
| 223 | | return x; |
|---|
| 224 | | } |
|---|
| | 208 | static unsigned FAT_entry (struct rdcf *f, unsigned cluster) |
|---|
| | 209 | { |
|---|
| | 210 | check_cluster (f, cluster); |
|---|
| | 211 | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
|---|
| | 212 | unsigned byte_index = cluster + (cluster >> 1); |
|---|
| | 213 | uint8_t p[2]; |
|---|
| | 214 | read_buffer (f, f->first_FAT_sector + byte_index / SECTOR_SIZE); |
|---|
| | 215 | p[0] = f->buffer.buf[byte_index % SECTOR_SIZE]; |
|---|
| | 216 | byte_index++; |
|---|
| | 217 | read_buffer (f, f->first_FAT_sector + byte_index / SECTOR_SIZE); |
|---|
| | 218 | p[1] = f->buffer.buf[byte_index % SECTOR_SIZE]; |
|---|
| | 219 | return (cluster & 1) ? (p[1] << 4 | p[0] >> 4) : (p[0] | p[1]) << 8 & |
|---|
| | 220 | 0xF00; |
|---|
| | 221 | } |
|---|
| | 222 | else { |
|---|
| | 223 | ushort x; |
|---|
| | 224 | read_buffer (f, f->first_FAT_sector + cluster / (SECTOR_SIZE / 2)); |
|---|
| | 225 | x = f->buffer.fat[cluster % (SECTOR_SIZE / 2)]; |
|---|
| | 226 | #ifdef _BIG_ENDIAN |
|---|
| | 227 | convert_short (x); |
|---|
| | 228 | #endif |
|---|
| | 229 | return x; |
|---|
| | 230 | } |
|---|
| 245 | | sector = f->first_FAT_sector; |
|---|
| 246 | | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
|---|
| 247 | | unsigned byte_index = cluster + (cluster>>1); |
|---|
| 248 | | uint8_t *p; |
|---|
| 249 | | read_buffer(f, sector + byte_index/SECTOR_SIZE); |
|---|
| 250 | | p = f->buffer.buf + byte_index%SECTOR_SIZE; |
|---|
| 251 | | *p = (cluster&1) ? (*p & 0x0F) | (x<<4) : x; |
|---|
| 252 | | f->buffer_status = DIRTY; |
|---|
| 253 | | read_buffer(f, sector + (byte_index+1)/SECTOR_SIZE); |
|---|
| 254 | | p = f->buffer.buf + (byte_index+1)%SECTOR_SIZE; |
|---|
| 255 | | *p = (cluster&1) ? x>>4 : (*p&0xF0) | (x>>8); |
|---|
| 256 | | } else { |
|---|
| 257 | | read_buffer(f, sector + cluster/(SECTOR_SIZE/2)); |
|---|
| 258 | | f->buffer.fat[cluster%(SECTOR_SIZE/2)] = x; |
|---|
| 259 | | } |
|---|
| 260 | | f->buffer_status = DIRTY; |
|---|
| | 251 | sector = f->first_FAT_sector; |
|---|
| | 252 | if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT) { |
|---|
| | 253 | unsigned byte_index = cluster + (cluster >> 1); |
|---|
| | 254 | uint8_t *p; |
|---|
| | 255 | read_buffer (f, sector + byte_index / SECTOR_SIZE); |
|---|
| | 256 | p = f->buffer.buf + byte_index % SECTOR_SIZE; |
|---|
| | 257 | *p = (cluster & 1) ? (*p & 0x0F) | (x << 4) : x; |
|---|
| | 258 | f->buffer_status = DIRTY; |
|---|
| | 259 | read_buffer (f, sector + (byte_index + 1) / SECTOR_SIZE); |
|---|
| | 260 | p = f->buffer.buf + (byte_index + 1) % SECTOR_SIZE; |
|---|
| | 261 | *p = (cluster & 1) ? x >> 4 : (*p & 0xF0) | (x >> 8); |
|---|
| | 262 | } |
|---|
| | 263 | else { |
|---|
| | 264 | read_buffer (f, sector + cluster / (SECTOR_SIZE / 2)); |
|---|
| | 265 | f->buffer.fat[cluster % (SECTOR_SIZE / 2)] = x; |
|---|
| | 266 | } |
|---|
| | 267 | f->buffer_status = DIRTY; |
|---|
| 269 | | static void check_file_character(struct rdcf *f, unsigned c) |
|---|
| 270 | | { |
|---|
| 271 | | static uint8_t table[32] = { |
|---|
| 272 | | 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC, |
|---|
| 273 | | 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90, |
|---|
| 274 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
|---|
| 275 | | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
|---|
| 276 | | }; |
|---|
| 277 | | if (table[c>>3] & 1<<(c&7)) error_exit(f, ~EINVAL); |
|---|
| | 276 | static void check_file_character (struct rdcf *f, unsigned c) |
|---|
| | 277 | { |
|---|
| | 278 | static uint8_t table[32] = { |
|---|
| | 279 | 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC, |
|---|
| | 280 | 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90, |
|---|
| | 281 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
|---|
| | 282 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
|---|
| | 283 | }; |
|---|
| | 284 | if (table[c >> 3] & 1 << (c & 7)) |
|---|
| | 285 | error_exit (f, ~EINVAL); |
|---|
| 286 | | static const char *spec_to_name_extension(struct rdcf *f, |
|---|
| 287 | | uint8_t *name_extension, const uint8_t *spec) |
|---|
| 288 | | { |
|---|
| 289 | | unsigned i = 0; |
|---|
| 290 | | unsigned c; |
|---|
| 291 | | while ((c=(*spec++))!=0 && c!=RDCF_SLASH_CHAR && c!='.') { |
|---|
| 292 | | check_file_character(f,c); |
|---|
| 293 | | if (i<NAME_SIZE) name_extension[i++] = toupper(c); |
|---|
| 294 | | } |
|---|
| 295 | | if (i==0) error_exit(f, ~EINVAL); |
|---|
| 296 | | while (i<NAME_SIZE) name_extension[i++] = ' '; |
|---|
| 297 | | if (c=='.') while ((c=(*spec++))!=0 && c!=RDCF_SLASH_CHAR) { |
|---|
| 298 | | check_file_character(f,c); |
|---|
| 299 | | if (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = toupper(c); |
|---|
| 300 | | } |
|---|
| 301 | | while (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = ' '; |
|---|
| 302 | | return spec-1; |
|---|
| | 294 | static const char *spec_to_name_extension (struct rdcf *f, |
|---|
| | 295 | uint8_t * name_extension, |
|---|
| | 296 | const uint8_t * spec) |
|---|
| | 297 | { |
|---|
| | 298 | unsigned i = 0; |
|---|
| | 299 | unsigned c; |
|---|
| | 300 | while ((c = (*spec++)) != 0 && c != RDCF_SLASH_CHAR && c != '.') { |
|---|
| | 301 | check_file_character (f, c); |
|---|
| | 302 | if (i < NAME_SIZE) |
|---|
| | 303 | name_extension[i++] = toupper (c); |
|---|
| | 304 | } |
|---|
| | 305 | if (i == 0) |
|---|
| | 306 | error_exit (f, ~EINVAL); |
|---|
| | 307 | while (i < NAME_SIZE) |
|---|
| | 308 | name_extension[i++] = ' '; |
|---|
| | 309 | if (c == '.') |
|---|
| | 310 | while ((c = (*spec++)) != 0 && c != RDCF_SLASH_CHAR) { |
|---|
| | 311 | check_file_character (f, c); |
|---|
| | 312 | if (i < NAME_SIZE + EXTENSION_SIZE) |
|---|
| | 313 | name_extension[i++] = toupper (c); |
|---|
| | 314 | } |
|---|
| | 315 | while (i < NAME_SIZE + EXTENSION_SIZE) |
|---|
| | 316 | name_extension[i++] = ' '; |
|---|
| | 317 | return spec - 1; |
|---|
| 309 | | static void name_extension_to_spec(uint8_t *spec, const uint8_t *name_extension) |
|---|
| 310 | | { |
|---|
| 311 | | unsigned i; |
|---|
| 312 | | uint8_t *s = spec; |
|---|
| 313 | | for (i=0; i<NAME_SIZE && name_extension[i]!=' '; i++) |
|---|
| 314 | | *s++ = name_extension[i]; |
|---|
| 315 | | if (name_extension[NAME_SIZE]!=' ') { |
|---|
| 316 | | *s++ = '.'; |
|---|
| 317 | | for (i=NAME_SIZE; |
|---|
| 318 | | i<NAME_SIZE+EXTENSION_SIZE && name_extension[i]!=' '; i++) { |
|---|
| 319 | | *s++ = name_extension[i]; |
|---|
| 320 | | } |
|---|
| 321 | | } |
|---|
| 322 | | *s = 0; |
|---|
| | 324 | static void name_extension_to_spec (uint8_t * spec, |
|---|
| | 325 | const uint8_t * name_extension) |
|---|
| | 326 | { |
|---|
| | 327 | unsigned i; |
|---|
| | 328 | uint8_t *s = spec; |
|---|
| | 329 | for (i = 0; i < NAME_SIZE && name_extension[i] != ' '; i++) |
|---|
| | 330 | *s++ = name_extension[i]; |
|---|
| | 331 | if (name_extension[NAME_SIZE] != ' ') { |
|---|
| | 332 | *s++ = '.'; |
|---|
| | 333 | for (i = NAME_SIZE; |
|---|
| | 334 | i < NAME_SIZE + EXTENSION_SIZE && name_extension[i] != ' '; i++) { |
|---|
| | 335 | *s++ = name_extension[i]; |
|---|
| | 336 | } |
|---|
| | 337 | } |
|---|
| | 338 | *s = 0; |
|---|
| 354 | | static void update_directory_entry(struct rdcf *f, int delete_entry) |
|---|
| 355 | | { |
|---|
| 356 | | struct directory *d = find_directory(f); |
|---|
| 357 | | if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.') |
|---|
| 358 | | memcpy(d->name_extension, f->file.spec, NAME_SIZE+EXTENSION_SIZE); |
|---|
| 359 | | else |
|---|
| 360 | | spec_to_name_extension(f, d->name_extension, f->file.spec); |
|---|
| 361 | | if (delete_entry) d->name_extension[0] = DELETED_FILE; |
|---|
| 362 | | d->attribute = f->file.attribute; |
|---|
| 363 | | d->date = (f->file.date_and_time.year-1980)<<9 | |
|---|
| 364 | | f->file.date_and_time.month<<5 | f->file.date_and_time.day; |
|---|
| 365 | | d->time = f->file.date_and_time.hour<<11 | |
|---|
| 366 | | f->file.date_and_time.minute<<5 | f->file.date_and_time.second>>1; |
|---|
| 367 | | d->first_cluster = f->file.first_cluster; |
|---|
| 368 | | d->size = f->file.size; |
|---|
| 369 | | memset(d->reserved, 0, sizeof(d->reserved)); |
|---|
| | 370 | static void update_directory_entry (struct rdcf *f, int delete_entry) |
|---|
| | 371 | { |
|---|
| | 372 | struct directory *d = find_directory (f); |
|---|
| | 373 | if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.') |
|---|
| | 374 | memcpy (d->name_extension, f->file.spec, NAME_SIZE + EXTENSION_SIZE); |
|---|
| | 375 | else |
|---|
| | 376 | spec_to_name_extension (f, d->name_extension, f->file.spec); |
|---|
| | 377 | if (delete_entry) |
|---|
| | 378 | d->name_extension[0] = DELETED_FILE; |
|---|
| | 379 | d->attribute = f->file.attribute; |
|---|
| | 380 | d->date = (f->file.date_and_time.year - 1980) << 9 | |
|---|
| | 381 | f->file.date_and_time.month << 5 | f->file.date_and_time.day; |
|---|
| | 382 | d->time = f->file.date_and_time.hour << 11 | |
|---|
| | 383 | f->file.date_and_time.minute << 5 | f->file.date_and_time.second >> 1; |
|---|
| | 384 | d->first_cluster = f->file.first_cluster; |
|---|
| | 385 | d->size = f->file.size; |
|---|
| | 386 | memset (d->reserved, 0, sizeof (d->reserved)); |
|---|
| 383 | | static void read_directory_entry(struct rdcf *f) |
|---|
| 384 | | { |
|---|
| 385 | | struct directory *d = find_directory(f); |
|---|
| 386 | | if (d->attribute&RDCF_VOLUME) { |
|---|
| 387 | | memcpy(f->file.spec, d->name_extension, NAME_SIZE+EXTENSION_SIZE); |
|---|
| 388 | | f->file.spec[NAME_SIZE+EXTENSION_SIZE] = 0; |
|---|
| 389 | | } else |
|---|
| 390 | | name_extension_to_spec(f->file.spec, d->name_extension); |
|---|
| 391 | | f->file.attribute = d->attribute; |
|---|
| 392 | | { |
|---|
| 393 | | ushort date = d->date; |
|---|
| | 400 | static void read_directory_entry (struct rdcf *f) |
|---|
| | 401 | { |
|---|
| | 402 | struct directory *d = find_directory (f); |
|---|
| | 403 | if (d->attribute & RDCF_VOLUME) { |
|---|
| | 404 | memcpy (f->file.spec, d->name_extension, NAME_SIZE + EXTENSION_SIZE); |
|---|
| | 405 | f->file.spec[NAME_SIZE + EXTENSION_SIZE] = 0; |
|---|
| | 406 | } |
|---|
| | 407 | else |
|---|
| | 408 | name_extension_to_spec (f->file.spec, d->name_extension); |
|---|
| | 409 | f->file.attribute = d->attribute; |
|---|
| | 410 | { |
|---|
| | 411 | ushort date = d->date; |
|---|
| 437 | | static int find_file_in_directory_or_find_volume(struct rdcf *f, |
|---|
| 438 | | const uint8_t *name_extension) |
|---|
| 439 | | { |
|---|
| 440 | | unsigned empty_cluster = 2; |
|---|
| 441 | | unsigned empty_index = NO_DIRECTORY_INDEX; |
|---|
| 442 | | unsigned number_of_directory_entries = f->directory_cluster == 0 ? |
|---|
| 443 | | (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR : |
|---|
| 444 | | ENTRIES_PER_SECTOR*f->sectors_per_cluster; |
|---|
| 445 | | f->directory_first_cluster = f->directory_cluster; |
|---|
| 446 | | while (1) { |
|---|
| 447 | | for (f->directory_index = 0; |
|---|
| 448 | | f->directory_index < number_of_directory_entries; f->directory_index++) { |
|---|
| 449 | | struct directory *d = find_directory(f); |
|---|
| 450 | | if ((d->name_extension[0] == DELETED_FILE || |
|---|
| 451 | | d->name_extension[0] == END_DIRECTORY) && |
|---|
| 452 | | empty_index == NO_DIRECTORY_INDEX) { |
|---|
| 453 | | empty_cluster = f->directory_cluster; |
|---|
| 454 | | empty_index = f->directory_index; |
|---|
| 455 | | } |
|---|
| 456 | | if (d->name_extension[0] == END_DIRECTORY) break; |
|---|
| 457 | | if ((name_extension == NULL && |
|---|
| 458 | | (d->name_extension[0] != DELETED_FILE && d->attribute & RDCF_VOLUME)) || |
|---|
| 459 | | (name_extension != NULL && |
|---|
| 460 | | (memcmp(d->name_extension, name_extension, NAME_SIZE+EXTENSION_SIZE) |
|---|
| 461 | | == 0 && (d->attribute&RDCF_VOLUME) == 0))) { |
|---|
| 462 | | read_directory_entry(f); |
|---|
| 463 | | return 1; |
|---|
| 464 | | } |
|---|
| 465 | | } |
|---|
| 466 | | if (f->directory_index < number_of_directory_entries || |
|---|
| 467 | | f->directory_cluster == 0) { |
|---|
| 468 | | break; |
|---|
| 469 | | } |
|---|
| 470 | | { |
|---|
| 471 | | unsigned x = FAT_entry(f, f->directory_cluster); |
|---|
| 472 | | if (x >= f->last_cluster_mark) break; |
|---|
| 473 | | f->directory_cluster = x; |
|---|
| 474 | | } |
|---|
| 475 | | } |
|---|
| 476 | | f->directory_index = empty_index; |
|---|
| 477 | | if (f->directory_index != NO_DIRECTORY_INDEX) |
|---|
| 478 | | f->directory_cluster = empty_cluster; |
|---|
| 479 | | if (name_extension != NULL) |
|---|
| 480 | | name_extension_to_spec(f->file.spec, name_extension); |
|---|
| 481 | | return 0; |
|---|
| | 455 | static int find_file_in_directory_or_find_volume (struct rdcf *f, |
|---|
| | 456 | const uint8_t * |
|---|
| | 457 | name_extension) |
|---|
| | 458 | { |
|---|
| | 459 | unsigned empty_cluster = 2; |
|---|
| | 460 | unsigned empty_index = NO_DIRECTORY_INDEX; |
|---|
| | 461 | unsigned number_of_directory_entries = f->directory_cluster == 0 ? |
|---|
| | 462 | (f->first_data_sector - f->first_directory_sector) * ENTRIES_PER_SECTOR : |
|---|
| | 463 | ENTRIES_PER_SECTOR * f->sectors_per_cluster; |
|---|
| | 464 | f->directory_first_cluster = f->directory_cluster; |
|---|
| | 465 | while (1) { |
|---|
| | 466 | for (f->directory_index = 0; |
|---|
| | 467 | f->directory_index < number_of_directory_entries; |
|---|
| | 468 | f->directory_index++) { |
|---|
| | 469 | struct directory *d = find_directory (f); |
|---|
| | 470 | if ((d->name_extension[0] == DELETED_FILE || |
|---|
| | 471 | d->name_extension[0] == END_DIRECTORY) && |
|---|
| | 472 | empty_index == NO_DIRECTORY_INDEX) { |
|---|
| | 473 | empty_cluster = f->directory_cluster; |
|---|
| | 474 | empty_index = f->directory_index; |
|---|
| | 475 | } |
|---|
| | 476 | if (d->name_extension[0] == END_DIRECTORY) |
|---|
| | 477 | break; |
|---|
| | 478 | if ((name_extension == NULL && |
|---|
| | 479 | (d->name_extension[0] != DELETED_FILE |
|---|
| | 480 | && d->attribute & RDCF_VOLUME)) || (name_extension != NULL |
|---|
| | 481 | && |
|---|
| | 482 | (memcmp |
|---|
| | 483 | (d->name_extension, |
|---|
| | 484 | name_extension, |
|---|
| | 485 | NAME_SIZE + EXTENSION_SIZE) |
|---|
| | 486 | == 0 |
|---|
| | 487 | && (d-> |
|---|
| | 488 | attribute & RDCF_VOLUME) |
|---|
| | 489 | == 0))) { |
|---|
| | 490 | read_directory_entry (f); |
|---|
| | 491 | return 1; |
|---|
| | 492 | } |
|---|
| | 493 | } |
|---|
| | 494 | if (f->directory_index < number_of_directory_entries || |
|---|
| | 495 | f->directory_cluster == 0) { |
|---|
| | 496 | break; |
|---|
| | 497 | } |
|---|
| | 498 | { |
|---|
| | 499 | unsigned x = FAT_entry (f, f->directory_cluster); |
|---|
| | 500 | if (x >= f->last_cluster_mark) |
|---|
| | 501 | break; |
|---|
| | 502 | f->directory_cluster = x; |
|---|
| | 503 | } |
|---|
| | 504 | } |
|---|
| | 505 | f->directory_index = empty_index; |
|---|
| | 506 | if (f->directory_index != NO_DIRECTORY_INDEX) |
|---|
| | 507 | f->directory_cluster = empty_cluster; |
|---|
| | 508 | if (name_extension != NULL) |
|---|
| | 509 | name_extension_to_spec (f->file.spec, name_extension); |
|---|
| | 510 | return 0; |
|---|
| 496 | | static int find_file(struct rdcf *f, const char *spec) |
|---|
| 497 | | { |
|---|
| 498 | | /* start with root directory */ |
|---|
| 499 | | f->directory_cluster = 0; |
|---|
| 500 | | while (1) { |
|---|
| 501 | | int found; |
|---|
| 502 | | uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; |
|---|
| 503 | | /* scan name and extension */ |
|---|
| 504 | | spec = spec_to_name_extension(f, name_extension, spec); |
|---|
| 505 | | /* look it up in directory */ |
|---|
| 506 | | found = find_file_in_directory_or_find_volume(f, name_extension); |
|---|
| 507 | | /* if this is the end of the file specification, return */ |
|---|
| 508 | | if (*spec == 0) return found; |
|---|
| 509 | | /* otherwise, the name and extension were a subdirectory in the path */ |
|---|
| 510 | | if (!found || (f->file.attribute&RDCF_DIRECTORY) == 0) |
|---|
| 511 | | error_exit(f, ~EISDIR); |
|---|
| 512 | | f->directory_cluster = f->file.first_cluster; |
|---|
| 513 | | /* skip over the \ after the subdirectory */ |
|---|
| 514 | | spec++; |
|---|
| 515 | | } |
|---|
| | 525 | static int find_file (struct rdcf *f, const char *spec) |
|---|
| | 526 | { |
|---|
| | 527 | /* start with root directory */ |
|---|
| | 528 | f->directory_cluster = 0; |
|---|
| | 529 | while (1) { |
|---|
| | 530 | int found; |
|---|
| | 531 | uint8_t name_extension[NAME_SIZE + EXTENSION_SIZE]; |
|---|
| | 532 | /* scan name and extension */ |
|---|
| | 533 | spec = spec_to_name_extension (f, name_extension, spec); |
|---|
| | 534 | /* look it up in directory */ |
|---|
| | 535 | found = find_file_in_directory_or_find_volume (f, name_extension); |
|---|
| | 536 | /* if this is the end of the file specification, return */ |
|---|
| | 537 | if (*spec == 0) |
|---|
| | 538 | return found; |
|---|
| | 539 | /* otherwise, the name and extension were a subdirectory in the path */ |
|---|
| | 540 | if (!found || (f->file.attribute & RDCF_DIRECTORY) == 0) |
|---|
| | 541 | error_exit (f, ~EISDIR); |
|---|
| | 542 | f->directory_cluster = f->file.first_cluster; |
|---|
| | 543 | /* skip over the \ after the subdirectory */ |
|---|
| | 544 | spec++; |
|---|
| | 545 | } |
|---|
| 522 | | static void read_file_system_information(struct rdcf *f) |
|---|
| 523 | | { |
|---|
| 524 | | f->first_FAT_sector = DriveDesc.FirstFatSector; |
|---|
| 525 | | f->sectors_per_FAT = DriveDesc.SectorsPerFAT; |
|---|
| 526 | | f->sectors_per_cluster = DriveDesc.SectorsPerCluster; |
|---|
| 527 | | f->first_directory_sector = DriveDesc.RootDirSector; |
|---|
| 528 | | f->first_data_sector = DriveDesc.DataStartSector; |
|---|
| 529 | | f->maximum_cluster_number = |
|---|
| 530 | | ((DriveDesc.MaxDataSector - DriveDesc.DataStartSector) / |
|---|
| 531 | | DriveDesc.SectorsPerCluster) + 1; |
|---|
| 532 | | f->last_cluster_mark = |
|---|
| 533 | | f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT ? |
|---|
| 534 | | LAST_CLUSTER_12_BIT : LAST_CLUSTER_16_BIT; |
|---|
| | 552 | static void read_file_system_information (struct rdcf *f) |
|---|
| | 553 | { |
|---|
| | 554 | f->first_FAT_sector = DriveDesc.FirstFatSector; |
|---|
| | 555 | f->sectors_per_FAT = DriveDesc.SectorsPerFAT; |
|---|
| | 556 | f->sectors_per_cluster = DriveDesc.SectorsPerCluster; |
|---|
| | 557 | f->first_directory_sector = DriveDesc.RootDirSector; |
|---|
| | 558 | f->first_data_sector = DriveDesc.DataStartSector; |
|---|
| | 559 | f->maximum_cluster_number = |
|---|
| | 560 | ((DriveDesc.MaxDataSector - DriveDesc.DataStartSector) / |
|---|
| | 561 | DriveDesc.SectorsPerCluster) + 1; |
|---|
| | 562 | f->last_cluster_mark = |
|---|
| | 563 | f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT ? |
|---|
| | 564 | LAST_CLUSTER_12_BIT : LAST_CLUSTER_16_BIT; |
|---|
| 584 | | static void release_FAT_entries(struct rdcf *f) |
|---|
| 585 | | { |
|---|
| 586 | | unsigned j; |
|---|
| 587 | | j = f->file.first_cluster; |
|---|
| 588 | | if (j != EMPTY_CLUSTER) { |
|---|
| 589 | | while (j < f->last_cluster_mark && j) { |
|---|
| 590 | | unsigned k = FAT_entry(f,j); |
|---|
| 591 | | if (j < f->first_possibly_empty_cluster) { |
|---|
| 592 | | f->first_possibly_empty_cluster = j; |
|---|
| 593 | | } |
|---|
| 594 | | set_FAT_entry(f,j,EMPTY_CLUSTER); |
|---|
| 595 | | j = k; |
|---|
| 596 | | } |
|---|
| 597 | | } |
|---|
| 598 | | f->mode |= WRITTEN; |
|---|
| | 616 | static void release_FAT_entries (struct rdcf *f) |
|---|
| | 617 | { |
|---|
| | 618 | unsigned j; |
|---|
| | 619 | j = f->file.first_cluster; |
|---|
| | 620 | if (j != EMPTY_CLUSTER) { |
|---|
| | 621 | while (j < f->last_cluster_mark && j) { |
|---|
| | 622 | unsigned k = FAT_entry (f, j); |
|---|
| | 623 | if (j < f->first_possibly_empty_cluster) { |
|---|
| | 624 | f->first_possibly_empty_cluster = j; |
|---|
| | 625 | } |
|---|
| | 626 | set_FAT_entry (f, j, EMPTY_CLUSTER); |
|---|
| | 627 | j = k; |
|---|
| | 628 | } |
|---|
| | 629 | } |
|---|
| | 630 | f->mode |= WRITTEN; |
|---|
| 607 | | static unsigned add_new_cluster(struct rdcf *f, unsigned cluster, |
|---|
| 608 | | unsigned first_possibly_empty_cluster) |
|---|
| 609 | | { |
|---|
| 610 | | unsigned new_cluster; |
|---|
| 611 | | for (new_cluster = first_possibly_empty_cluster; |
|---|
| 612 | | new_cluster <= f->maximum_cluster_number; new_cluster++) { |
|---|
| 613 | | if (FAT_entry(f, new_cluster)==EMPTY_CLUSTER) break; |
|---|
| 614 | | } |
|---|
| 615 | | if (new_cluster > f->maximum_cluster_number) return EMPTY_CLUSTER; |
|---|
| 616 | | if (cluster != EMPTY_CLUSTER) set_FAT_entry(f, cluster, new_cluster); |
|---|
| 617 | | set_FAT_entry(f, new_cluster, f->last_cluster_mark); |
|---|
| 618 | | return new_cluster; |
|---|
| | 639 | static unsigned add_new_cluster (struct rdcf *f, unsigned cluster, |
|---|
| | 640 | unsigned first_possibly_empty_cluster) |
|---|
| | 641 | { |
|---|
| | 642 | unsigned new_cluster; |
|---|
| | 643 | for (new_cluster = first_possibly_empty_cluster; |
|---|
| | 644 | new_cluster <= f->maximum_cluster_number; new_cluster++) { |
|---|
| | 645 | if (FAT_entry (f, new_cluster) == EMPTY_CLUSTER) |
|---|
| | 646 | break; |
|---|
| | 647 | } |
|---|
| | 648 | if (new_cluster > f->maximum_cluster_number) |
|---|
| | 649 | return EMPTY_CLUSTER; |
|---|
| | 650 | if (cluster != EMPTY_CLUSTER) |
|---|
| | 651 | set_FAT_entry (f, cluster, new_cluster); |
|---|
| | 652 | set_FAT_entry (f, new_cluster, f->last_cluster_mark); |
|---|
| | 653 | return new_cluster; |
|---|
| 625 | | static void clear_cluster(struct rdcf *f, unsigned cluster) |
|---|
| 626 | | { |
|---|
| 627 | | unsigned count = f->sectors_per_cluster; |
|---|
| 628 | | unsigned sector = first_sector_in_cluster(f, cluster); |
|---|
| 629 | | flush_buffer(f); |
|---|
| 630 | | f->buffer_status = EMPTY; |
|---|
| 631 | | memset(f->buffer.buf, 0, SECTOR_SIZE); |
|---|
| 632 | | do write_sector(f, sector++, f->buffer.buf); while (--count != 0); |
|---|
| | 660 | static void clear_cluster (struct rdcf *f, unsigned cluster) |
|---|
| | 661 | { |
|---|
| | 662 | unsigned count = f->sectors_per_cluster; |
|---|
| | 663 | unsigned sector = first_sector_in_cluster (f, cluster); |
|---|
| | 664 | flush_buffer (f); |
|---|
| | 665 | f->buffer_status = EMPTY; |
|---|
| | 666 | memset (f->buffer.buf, 0, SECTOR_SIZE); |
|---|
| | 667 | do |
|---|
| | 668 | write_sector (f, sector++, f->buffer.buf); |
|---|
| | 669 | while (--count != 0); |
|---|
| 640 | | static void lengthen_directory_if_necessary(struct rdcf *f) |
|---|
| 641 | | { |
|---|
| 642 | | if (f->directory_index == NO_DIRECTORY_INDEX) { |
|---|
| 643 | | if (f->directory_cluster == 0) error_exit(f, ~ENOSPC); |
|---|
| 644 | | f->directory_cluster = add_new_cluster(f, f->directory_cluster, 2); |
|---|
| 645 | | if (f->directory_cluster == 0) error_exit(f, ~ENOSPC); |
|---|
| 646 | | f->directory_index = 0; |
|---|
| 647 | | clear_cluster(f, f->directory_cluster); |
|---|
| 648 | | } |
|---|
| | 677 | static void lengthen_directory_if_necessary (struct rdcf *f) |
|---|
| | 678 | { |
|---|
| | 679 | if (f->directory_index == NO_DIRECTORY_INDEX) { |
|---|
| | 680 | if (f->directory_cluster == 0) |
|---|
| | 681 | error_exit (f, ~ENOSPC); |
|---|
| | 682 | f->directory_cluster = add_new_cluster (f, f->directory_cluster, 2); |
|---|
| | 683 | if (f->directory_cluster == 0) |
|---|
| | 684 | error_exit (f, ~ENOSPC); |
|---|
| | 685 | f->directory_index = 0; |
|---|
| | 686 | clear_cluster (f, f->directory_cluster); |
|---|
| | 687 | } |
|---|
| 660 | | int rdcf_close(struct rdcf *f) |
|---|
| 661 | | { |
|---|
| 662 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
|---|
| 663 | | if (f->mode & WRITTEN) { |
|---|
| 664 | | f->buffer_status = EMPTY; |
|---|
| 665 | | f->file.attribute |= RDCF_ARCHIVE; |
|---|
| 666 | | rdcf_get_date_and_time(&f->file.date_and_time); |
|---|
| 667 | | // do not allow empty files. |
|---|
| 668 | | update_directory_entry(f, (f->file.size) ? 0 : 1); |
|---|
| 669 | | flush_buffer(f); |
|---|
| 670 | | } |
|---|
| 671 | | return 0; |
|---|
| 672 | | } |
|---|
| 673 | | |
|---|
| 674 | | int rdcf_delete(struct rdcf *f, const char *spec) |
|---|
| 675 | | { |
|---|
| 676 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
|---|
| 677 | | spec = initialize_fcb(f, spec); |
|---|
| 678 | | if (*spec == 0) /* delete volume label */ { |
|---|
| 679 | | if (!find_file_in_directory_or_find_volume(f, NULL)) |
|---|
| 680 | | error_exit(f, ~ENOENT); |
|---|
| 681 | | } else if (!find_file(f, spec)) |
|---|
| 682 | | error_exit(f, ~ENOENT); |
|---|
| 683 | | /* check to see that a directory is empty before deleting it */ |
|---|
| 684 | | else if (f->file.attribute & RDCF_DIRECTORY) { |
|---|
| 685 | | unsigned cluster = f->file.first_cluster; |
|---|
| 686 | | while (cluster != EMPTY_CLUSTER && cluster < f->last_cluster_mark) { |
|---|
| 687 | | unsigned sector = first_sector_in_cluster(f, cluster); |
|---|
| 688 | | unsigned sector_count = f->sectors_per_cluster; |
|---|
| 689 | | unsigned next_cluster = FAT_entry(f, cluster); |
|---|
| 690 | | do { |
|---|
| 691 | | unsigned entry_count = ENTRIES_PER_SECTOR; |
|---|
| 692 | | uint8_t *p = f->buffer.buf; |
|---|
| 693 | | read_buffer(f, sector); |
|---|
| 694 | | do { |
|---|
| 695 | | unsigned c = *p; |
|---|
| 696 | | if (c == END_DIRECTORY) break; |
|---|
| 697 | | if (c != DELETED_FILE && c != '.') error_exit(f, ~EACCES); |
|---|
| 698 | | p += sizeof(struct directory); |
|---|
| 699 | | } while (--entry_count != 0); |
|---|
| 700 | | if (entry_count != 0) break; |
|---|
| 701 | | } while (--sector_count != 0); |
|---|
| 702 | | cluster = next_cluster; |
|---|
| 703 | | } |
|---|
| 704 | | } else check_write_access(f); |
|---|
| 705 | | release_FAT_entries(f); |
|---|
| 706 | | update_directory_entry(f, 1); |
|---|
| 707 | | flush_buffer(f); |
|---|
| 708 | | return 0; |
|---|
| 709 | | } |
|---|
| 710 | | |
|---|
| 711 | | int rdcf_rename(struct rdcf *f, const char *old_spec, const char *new_spec) |
|---|
| 712 | | { |
|---|
| 713 | | uint8_t name_extension[NAME_SIZE+EXTENSION_SIZE]; |
|---|
| 714 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
|---|
| 715 | | old_spec = initialize_fcb(f, old_spec); |
|---|
| 716 | | if (!find_file(f, old_spec)) error_exit(f, ~ENOENT); |
|---|
| 717 | | if (f->file.attribute & (RDCF_HIDDEN+RDCF_SYSTEM)) |
|---|
| 718 | | error_exit(f, ~EACCES); |
|---|
| 719 | | spec_to_name_extension(f, name_extension, new_spec); |
|---|
| 720 | | f->directory_cluster = f->directory_first_cluster; |
|---|
| 721 | | if (find_file_in_directory_or_find_volume(f, name_extension)) |
|---|
| 722 | | error_exit(f, ~ENOENT); |
|---|
| 723 | | find_file(f, old_spec); |
|---|
| 724 | | name_extension_to_spec(f->file.spec, name_extension); |
|---|
| 725 | | update_directory_entry(f, 0); |
|---|
| 726 | | flush_buffer(f); |
|---|
| 727 | | return 0; |
|---|
| 728 | | } |
|---|
| 729 | | |
|---|
| 730 | | int rdcf_open(struct rdcf *f, const char *spec, unsigned mode) |
|---|
| 731 | | { |
|---|
| 732 | | int found; |
|---|
| 733 | | if ((f->result=setjmp(f->error)) != 0) return f->result; |
|---|
| 734 | | // make sure it is not open on another stream. |
|---|
| 735 | | f->mode = 0; |
|---|
| 736 | | found = find_file(f, initialize_fcb(f, spec)); |
|---|
| 737 | | if (found && f->file.attribute&RDCF_DIRECTORY) |
|---|
| 738 | | error_exit(f, ~EISDIR); |
|---|
| 739 | | // see how to open the file. |
|---|
| 740 | | if (mode & _FTRUNC && found) { |
|---|
| 741 | | // simply empty the file of clusters. |
|---|
| 742 | | if (found) { |
|---|
| 743 | | check_write_access(f); |
|---|
| 744 | | release_FAT_entries(f); |
|---|
| 745 | | flush_buffer(f); |
|---|
| 746 | | } else lengthen_directory_if_necessary(f); |
|---|
| 747 | | f->file.first_cluster = EMPTY_CLUSTER; |
|---|
| 748 | | f->file.size = 0L; |
|---|
| 749 | | } |
|---|
| 750 | | // create new dir entry if mode is not purely read: "r" |
|---|
| 751 | | if (!found && (mode & 0x3)) { |
|---|
| 752 | | f->file.attribute = RDCF_ARCHIVE; |
|---|
| 753 | | rdcf_get_date_and_time(&f->file.date_and_time); |
|---|
| 754 | | f->mode |= WRITTEN; |
|---|
| 755 | | f->file.first_cluster = EMPTY_CLUSTER; |
|---|
| 756 | | f->file.size = 0L; |
|---|
| 757 | | update_directory_entry(f, 0); |
|---|
| 758 | | flush_buffer(f); |
|---|
| 759 | | } |
|---|
| 760 | | if ((mode & 0x3) == 0) { |
|---|
| 761 | | |
|---|