Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / fs / fat / fat.c
blob58a218dffe5ff38629c7eaa8fd6677f12da73924
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2008 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <aros/macros.h>
14 #include <exec/errors.h>
15 #include <exec/types.h>
16 #include <dos/dos.h>
17 #include <dos/dosextens.h>
18 #include <dos/filehandler.h>
20 #include <proto/exec.h>
21 #include <proto/dos.h>
23 #include <clib/macros.h>
25 #include <string.h>
26 #include <ctype.h>
28 #include "fat_fs.h"
29 #include "fat_protos.h"
30 #include "timer.h"
32 #define DEBUG DEBUG_MISC
33 #include "debug.h"
35 /* helper function to get the location of a fat entry for a cluster. it used
36 * to be a define until it got too crazy */
37 static UBYTE *GetFatEntryPtr(struct FSSuper *sb, ULONG offset, struct cache_block **rb) {
38 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
39 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
41 /* if the target cluster is not within the currently loaded chunk of fat,
42 * we need to get the right data in */
43 if (sb->fat_cache_block != entry_cache_block) {
44 D(bug("[fat] loading %ld FAT sectors starting at sector %ld\n", sb->fat_blocks_count, entry_cache_block << (sb->fat_cachesize_bits - sb->sectorsize_bits)));
45 /* put the old ones back */
46 if (sb->fat_cache_block != 0xffffffff)
47 cache_put_blocks(sb->cache, sb->fat_blocks, sb->fat_blocks_count, 0);
49 /* load some more */
50 cache_get_blocks(sb->cache,
51 sb->first_device_sector + sb->first_fat_sector +
52 (entry_cache_block << (sb->fat_cachesize_bits - sb->sectorsize_bits)),
53 sb->fat_blocks_count,
55 sb->fat_blocks);
57 /* remember where we are for next time */
58 sb->fat_cache_block = entry_cache_block;
61 /* give the block back if they asked for it (needed to mark the block
62 * dirty if they're writing */
63 if (rb != NULL)
64 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
66 /* compute the pointer location and return it */
67 return sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits]->data +
68 (entry_cache_offset & (sb->sectorsize-1));
71 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
72 * entries are condensed into three bytes, like so:
74 * entry: aaaaaaaa aaaabbbb bbbbbbbb
75 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
77 * To get at the entry we want, we find and grab the word starting at either
78 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
79 * 1.03 p16-17 describes the method
81 * The only tricky bit is if the word falls such that the first byte is the
82 * last byte of the block and the second byte is the first byte of the next
83 * block. Since our block data are stored within cache block structures, a
84 * simple cast won't do (hell, the second block may not even be in memory if
85 * we're at the end of the FAT cache). So we get it a byte at a time, and
86 * build the word ourselves.
88 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n) {
89 ULONG offset = n + n/2;
90 UWORD val;
92 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
93 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
95 val = *GetFatEntryPtr(sb, offset + 1, NULL) << 8;
96 val |= *GetFatEntryPtr(sb, offset, NULL);
98 else
99 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL)));
101 if (n & 1)
102 val >>= 4;
103 else
104 val &= 0xfff;
106 return val;
110 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
111 * word/long casts are fine. There's also no chance that the entry can be
112 * split across blocks. Why can't everything be this simple?
114 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n) {
115 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL)));
118 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n) {
119 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL)));
122 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val) {
123 struct cache_block *b;
124 ULONG offset = n + n/2;
125 BOOL boundary = FALSE;
126 UWORD *fat, newval;
128 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
129 boundary = TRUE;
131 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
133 newval = *GetFatEntryPtr(sb, offset + 1, NULL) << 8;
134 newval |= *GetFatEntryPtr(sb, offset, NULL);
136 else {
137 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b);
138 newval = AROS_LE2WORD(*fat);
141 if (n & 1) {
142 val <<= 4;
143 newval = (newval & 0xf) | val;
145 else {
146 newval = (newval & 0xf000) | val;
149 if (boundary) {
150 /* XXX ideally we'd mark both blocks dirty at the same time or only do
151 * it once if they're the same block. unfortunately b is essentially
152 * invalid after a call to GetFatEntryPtr, as it may have swapped the
153 * previous cache out. This is probably safe enough. */
154 *GetFatEntryPtr(sb, offset+1, &b) = newval >> 8;
155 cache_mark_block_dirty(sb->cache, b);
156 *GetFatEntryPtr(sb, offset, &b) = newval & 0xff;
157 cache_mark_block_dirty(sb->cache, b);
159 else {
160 *fat = AROS_WORD2LE(newval);
161 cache_mark_block_dirty(sb->cache, b);
165 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val) {
166 struct cache_block *b;
168 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b)) = AROS_WORD2LE((UWORD) val);
170 cache_mark_block_dirty(sb->cache, b);
173 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val) {
174 struct cache_block *b;
175 ULONG *fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b);
177 *fat = (*fat & 0xf0000000) | val;
179 cache_mark_block_dirty(sb->cache, b);
182 LONG ReadFATSuper(struct FSSuper *sb ) {
183 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
184 LONG err;
185 ULONG bsize = de->de_SizeBlock * 4;
186 struct FATBootSector *boot;
187 BOOL invalid = FALSE;
188 ULONG end;
190 D(bug("[fat] reading boot sector\n"));
192 boot = AllocMem(bsize, MEMF_ANY);
193 if (!boot)
194 return ERROR_NO_FREE_STORE;
196 * Read the boot sector. We go direct because we don't have a cache yet,
197 * and can't create one until we know the sector size, which is held in
198 * the boot sector. In practice it doesn't matter - we're going use this
199 * once and once only.
202 sb->first_device_sector = de->de_BlocksPerTrack *
203 de->de_LowCyl;
205 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
207 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
208 D(bug("[fat] couldn't read boot block (%ld)\n", err));
209 FreeMem(boot, bsize);
210 return err;
213 D(bug("\tBoot sector:\n"));
215 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
216 sb->sectorsize_bits = log2(sb->sectorsize);
217 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
218 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
220 sb->cluster_sectors = boot->bpb_sect_per_clust;
221 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
222 sb->clustersize_bits = log2(sb->clustersize);
223 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
225 D(bug("\tSectorsPerCluster = %ld\n", (ULONG)boot->bpb_sect_per_clust));
226 D(bug("\tClusterSize = %ld\n", sb->clustersize));
227 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
228 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
230 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
231 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
233 if (boot->bpb_fat_size_16 != 0)
234 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
235 else
236 sb->fat_size = AROS_LE2LONG(boot->type.fat32.bpb_fat_size_32);
237 D(bug("\tFAT Size = %ld\n", sb->fat_size));
239 if (boot->bpb_total_sectors_16 != 0)
240 sb->total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
241 else
242 sb->total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
243 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
245 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count) * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1)) >> sb->sectorsize_bits;
246 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
248 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector + (boot->bpb_num_fats * sb->fat_size) + sb->rootdir_sectors);
249 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
251 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
252 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
254 sb->first_rootdir_sector = sb->first_fat_sector + (boot->bpb_num_fats * sb->fat_size);
255 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
257 sb->first_data_sector = sb->first_fat_sector + (boot->bpb_num_fats * sb->fat_size) + sb->rootdir_sectors;
258 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
260 sb->free_clusters = 0xffffffff;
262 /* check if disk is in fact FAT filesystem */
264 /* valid sector size: 512, 1024, 2048, 4096 */
265 if (sb->sectorsize != 512 && sb->sectorsize != 1024 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
266 invalid = TRUE;
268 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
269 if ((boot->bpb_sect_per_clust & (boot->bpb_sect_per_clust - 1)) != 0 || boot->bpb_sect_per_clust == 0 || boot->bpb_sect_per_clust > 128)
270 invalid = TRUE;
272 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
273 if (sb->clustersize > 64 * 1024)
274 invalid = TRUE;
276 if (sb->first_fat_sector == 0)
277 invalid = TRUE;
279 if (boot->bpb_num_fats == 0)
280 invalid = TRUE;
282 if (boot->bpb_media < 0xF0)
283 invalid = TRUE;
285 /* FAT "signature" */
286 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
287 invalid = TRUE;
289 if (invalid) {
290 D(bug("\tInvalid FAT Boot Sector\n"));
291 FreeMem(boot, bsize);
292 return ERROR_NOT_A_DOS_DISK;
294 end = 0xFFFFFFFF / sb->sectorsize;
295 if ((sb->first_device_sector + sb->total_sectors - 1 > end) && (glob->readcmd == CMD_READ)) {
296 D(bug("\tDevice is too large\n"));
297 FreeMem(boot, bsize);
298 return IOERR_BADADDRESS;
301 sb->cache = cache_new(64, 256, sb->sectorsize, CACHE_WRITETHROUGH);
303 if (sb->clusters_count < 4085) {
304 D(bug("\tFAT12 filesystem detected\n"));
305 sb->type = 12;
306 sb->eoc_mark = 0x0FF8;
307 sb->func_get_fat_entry = GetFat12Entry;
308 sb->func_set_fat_entry = SetFat12Entry;
310 else if (sb->clusters_count < 65525) {
311 D(bug("\tFAT16 filesystem detected\n"));
312 sb->type = 16;
313 sb->eoc_mark = 0xFFF8;
314 sb->func_get_fat_entry = GetFat16Entry;
315 sb->func_set_fat_entry = SetFat16Entry;
317 else {
318 D(bug("\tFAT32 filesystem detected\n"));
319 sb->type = 32;
320 sb->eoc_mark = 0x0FFFFFF8;
321 sb->func_get_fat_entry = GetFat32Entry;
322 sb->func_set_fat_entry = SetFat32Entry;
325 /* setup the FAT cache and load the first blocks */
326 sb->fat_cachesize = 4096;
327 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
328 sb->fat_cache_block = 0xffffffff;
330 sb->fat_blocks_count = MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
331 sb->fat_blocks = AllocVecPooled(glob->mempool, sizeof(struct cache_block *) * sb->fat_blocks_count);
333 if (sb->type != 32) { /* FAT 12/16 */
334 /* setup volume id */
335 sb->volume_id = AROS_LE2LONG(boot->type.fat16.bs_volid);
337 /* location of root directory */
338 sb->rootdir_cluster = 0;
339 sb->rootdir_sector = sb->first_rootdir_sector;
341 else {
342 /* setup volume id */
343 sb->volume_id = AROS_LE2LONG(boot->type.fat32.bs_volid);
345 /* location of root directory */
346 sb->rootdir_cluster = AROS_LE2LONG(boot->type.fat32.bpb_root_cluster);
347 sb->rootdir_sector = 0;
350 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster, sb->rootdir_sector));
352 if (GetVolumeInfo(sb, &(sb->volume)) != 0) {
353 LONG i;
354 UBYTE *uu = (void *)&sb->volume_id;
356 for (i=1; i<10;) {
357 int d;
359 if (i==5)
360 sb->volume.name[i++]='-';
362 d = (*uu) & 0x0f;
363 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
364 d = ((*uu) & 0xf0)>>4;
365 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
367 uu++;
370 sb->volume.name[i] = '\0';
371 sb->volume.name[0] = 9;
374 NEWLIST(&(sb->locks));
376 sb->root_lock.dir_cluster = FAT_ROOTDIR_MARK;
377 sb->root_lock.dir_entry = FAT_ROOTDIR_MARK;
378 sb->root_lock.access = SHARED_LOCK;
379 sb->root_lock.first_cluster = 0;
380 sb->root_lock.attr = ATTR_DIRECTORY;
381 sb->root_lock.size = 0;
382 CopyMem(sb->volume.name, sb->root_lock.name, sb->volume.name[0]+1);
383 NEWLIST(&sb->root_lock.locks);
385 NEWLIST(&(sb->notifies));
387 D(bug("\tFAT Filesystem succesfully detected.\n"));
388 FreeMem(boot, bsize);
389 return 0;
392 LONG GetVolumeInfo(struct FSSuper *sb, struct VolumeInfo *volume) {
393 struct DirHandle dh;
394 struct DirEntry de;
395 LONG err;
396 int i;
398 D(bug("[fat] searching root directory for volume name\n"));
400 /* search the directory for the volume id entry. it would've been nice to
401 * just use GetNextDirEntry but I didn't want a flag or something to tell
402 * it not to skip the volume name */
403 InitDirHandle(sb, 0, &dh, FALSE);
405 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
407 /* match the volume id entry */
408 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID) {
409 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
411 /* copy the name in. volume->name is a BSTR */
413 volume->name[1] = de.e.entry.name[0];
414 volume->name[12] = '\0';
416 for (i = 1; i < 11; i++)
417 volume->name[i+1] = tolower(de.e.entry.name[i]);
419 for (i = 10; i > 1; i--)
420 if (volume->name[i+1] == ' ')
421 volume->name[i+1] = '\0';
423 volume->name[0] = strlen(&(volume->name[1]));
425 /* get the volume creation date date too */
426 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time, &volume->create_time);
428 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
430 break;
433 /* bail out if we hit the end of the dir */
434 if (de.e.entry.name[0] == 0x00) {
435 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
436 err = ERROR_OBJECT_NOT_FOUND;
437 break;
441 ReleaseDirHandle(&dh);
442 return err;
445 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name) {
446 struct DirHandle dh;
447 struct DirEntry de;
448 LONG err;
449 int i;
451 D(bug("[fat] searching root directory for volume name\n"));
453 /* search the directory for the volume id entry. it would've been nice to
454 * just use GetNextDirEntry but I didn't want a flag or something to tell
455 * it not to skip the volume name */
456 InitDirHandle(sb, 0, &dh, FALSE);
458 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
460 /* match the volume id entry */
461 if ((de.e.entry.attr & ATTR_LONG_NAME_MASK) == ATTR_VOLUME_ID) {
462 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
464 /* copy the name in. name is a BSTR */
465 de.e.entry.name[0] = name[1];
466 for (i = 1; i < 11; i++)
467 if (i < name[0])
468 de.e.entry.name[i] = tolower(name[i+1]);
469 else
470 de.e.entry.name[i] = ' ';
472 if ((err = UpdateDirEntry(&de)) != 0) {
473 D(bug("[fat] couldn't change volume name\n"));
474 return err;
477 sb->volume.name[0] = name[0] < 10 ? name[0] : 10;
478 CopyMem(&name[1], &(sb->volume.name[1]), sb->volume.name[0]);
479 sb->volume.name[sb->volume.name[0]+1] = '\0';
481 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
483 break;
486 /* bail out if we hit the end of the dir */
487 if (de.e.entry.name[0] == 0x00) {
488 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
489 err = ERROR_OBJECT_NOT_FOUND;
490 break;
494 ReleaseDirHandle(&dh);
495 return err;
498 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster) {
499 ULONG cluster = 0;
502 * XXX this implementation is extremely naive. things we
503 * could do to make it better:
505 * - don't start looking for a free cluster at the start
506 * each time. start from the current cluster and wrap
507 * around when we hit the end
508 * - track where we last found a free cluster and start
509 * from there
510 * - allocate several contiguous clusters at a time to
511 * reduce fragmentation
514 for (cluster = 2; cluster < sb->clusters_count && GET_NEXT_CLUSTER(sb, cluster) != 0; cluster++);
516 if (cluster == sb->clusters_count) {
517 D(bug("[fat] no more free clusters, we're out of space\n"));
518 return ERROR_DISK_FULL;
521 D(bug("[fat] found free cluster %ld\n", cluster));
523 *rcluster = cluster;
525 return 0;
528 void FreeFATSuper(struct FSSuper *sb) {
529 D(bug("\tRemoving Super Block from memory\n"));
530 cache_free(sb->cache);
531 FreeVecPooled(glob->mempool, sb->fat_blocks);
532 sb->fat_blocks = NULL;
535 LONG CompareFATSuper(struct FSSuper *s1, struct FSSuper *s2) {
536 LONG res;
538 if ((res = memcmp(s1->volume.name, s2->volume.name, s1->volume.name[0])) != 0)
539 return res;
541 return s1->volume_id - s2->volume_id;
545 /* see how many unused clusters are available */
546 void CountFreeClusters(struct FSSuper *sb) {
547 ULONG cluster = 0;
548 ULONG free = 0;
550 /* don't calculate this if we already have it */
551 if (sb->free_clusters != 0xffffffff)
552 return;
554 /* loop over all the data clusters */
555 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
557 /* record the free ones */
558 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
559 free++;
561 /* put the value away for later */
562 sb->free_clusters = free;
564 D(bug("\tfree clusters: %ld\n", free));
567 static const UWORD mdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 143, 273, 304, 334 };
569 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds) {
570 UBYTE year, month, day, hours, mins, secs;
571 UBYTE nleap;
573 /* date bits: yyyy yyym mmmd dddd */
574 year = (date & 0xfe00) >> 9; /* bits 15-9 */
575 month = (date & 0x01e0) >> 5; /* bits 8-5 */
576 day = date & 0x001f; /* bits 4-0 */
578 /* time bits: hhhh hmmm mmms ssss */
579 hours = (time & 0xf800) >> 11; /* bits 15-11 */
580 mins = (time & 0x07e0) >> 5; /* bits 8-5 */
581 secs = time & 0x001f; /* bits 4-0 */
583 D(bug("[fat] converting fat date: year %d month %d day %d hours %d mins %d secs %d\n", year, month, day, hours, mins, secs));
585 /* number of leap years in before this year. note this is only dividing by
586 * four, which is fine because FAT dates range 1980-2107. The only year in
587 * that range that is divisible by four but not a leap year is 2100. If
588 * this code is still being used then, feel free to fix it :) */
589 nleap = year >> 2;
591 /* if this year is a leap year and it's March or later, adjust for this
592 * year too */
593 if (year & 0x03 && month >= 3)
594 nleap++;
596 /* calculate days since 1978-01-01 (DOS epoch):
597 * 730 days in 1978+1979, getting us to the FAT epoch 1980-01-01
598 * years * 365 days
599 * leap days
600 * days in all the months before this one
601 * day of this month */
602 ds->ds_Days = 730 + year * 365 + nleap + mdays[month-1] + day-1;
604 /* minutes since midnight */
605 ds->ds_Minute = hours * 60 + mins;
607 /* 1/50 sec ticks. FAT dates are 0-29, so we have to multiply them by two
608 * as well */
609 ds->ds_Tick = (secs << 1) * TICKS_PER_SECOND;
611 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
614 void ConvertAROSDate(struct DateStamp ds, UWORD *date, UWORD *time) {
615 UBYTE year, month, day, hours, mins, secs;
616 BOOL leap;
618 /* converting no. of days since 1978-01-01 (DOS epoch) to
619 * years/months/days since FAT epoch (1980-01-01) */
621 /* subtract 730 days in 1978/1979 */
622 ds.ds_Days -= 730;
624 /* years are 365 days */
625 year = ds.ds_Days / 365;
626 ds.ds_Days -= year * 365;
628 /* leap years. same algorithm as above. get the number of leap years
629 * before this year, and subtract that many days */
630 ds.ds_Days -= year >> 2;
632 /* figure out if we need to adjust for a leap year this year. day 60 is
633 * 29-Feb/1-Mar */
634 leap = (year & 0x03 && ds.ds_Days >= 60);
636 /* find the month by checking it against the days-in-month array */
637 for (month = 1; month < 12 && ds.ds_Days > mdays[month]; month++);
639 /* day of month is whatever's left (+1, since we count from the 1st) */
640 day = ds.ds_Days - mdays[month - 1] + 1;
642 /* subtract a day if we're after march in a leap year */
643 if (leap) {
644 day--;
645 if (day == 0) {
646 month--;
647 if (month == 2)
648 day = 29;
649 else
650 day = ds.ds_Days - mdays[month] + 1;
654 /* time is easy by comparison. convert minutes since midnight to
655 * hours and seconds */
656 hours = ds.ds_Minute / 60;
657 mins = ds.ds_Minute - (hours * 60);
659 /* FAT seconds are 0-29 */
660 secs = (ds.ds_Tick / TICKS_PER_SECOND) >> 1;
662 /* all that remains is to bit-encode the whole lot */
664 /* date bits: yyyy yyym mmmd dddd */
665 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
667 /* time bits: hhhh hmmm mmms ssss */
668 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;