Subversion Repositories Kolibri OS

Rev

Rev 10051 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2288 clevermous 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
10051 ace_dent 3
;; Copyright (C) KolibriOS team 2011-2024. All rights reserved. ;;
2288 clevermous 4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
8
 
9
; =============================================================================
10
; ================================= Constants =================================
11
; =============================================================================
12
; Error codes for callback functions.
13
DISK_STATUS_OK              = 0 ; success
14
DISK_STATUS_GENERAL_ERROR   = -1; if no other code is suitable
15
DISK_STATUS_INVALID_CALL    = 1 ; invalid input parameters
16
DISK_STATUS_NO_MEDIA        = 2 ; no media present
17
DISK_STATUS_END_OF_MEDIA    = 3 ; end of media while reading/writing data
4437 clevermous 18
DISK_STATUS_NO_MEMORY       = 4 ; insufficient memory for driver operation
2288 clevermous 19
; Driver flags. Represent bits in DISK.DriverFlags.
20
DISK_NO_INSERT_NOTIFICATION = 1
21
; Media flags. Represent bits in DISKMEDIAINFO.Flags.
22
DISK_MEDIA_READONLY = 1
23
 
24
; If too many partitions are detected,there is probably an error on the disk.
25
; 256 partitions should be enough for any reasonable use.
26
; Also, the same number is limiting the number of MBRs to process; if
27
; too many MBRs are visible,there probably is a loop in the MBR structure.
28
MAX_NUM_PARTITIONS = 256
29
 
30
; =============================================================================
31
; ================================ Structures =================================
32
; =============================================================================
33
; This structure defines all callback functions for working with the physical
34
; device. They are implemented by a driver. Objects with this structure reside
35
; in a driver.
2381 hidnplayr 36
struct  DISKFUNC
37
        strucsize       dd ?
2288 clevermous 38
; Size of the structure. This field is intended for possible extensions of
39
; this structure. If a new function is added to this structure and a driver
40
; implements an old version, the caller can detect this by checking .strucsize,
41
; so the driver remains compatible.
2381 hidnplayr 42
        close           dd ?
2288 clevermous 43
; The pointer to the function which frees all driver-specific resources for
44
; the disk.
45
; Optional, may be NULL.
46
; void close(void* userdata);
2381 hidnplayr 47
        closemedia      dd ?
2288 clevermous 48
; The pointer to the function which informs the driver that the kernel has
49
; finished all processing with the current media. If media is removed, the
50
; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA,
51
; even if new media is inserted, until this function is called. If media is
52
; removed, a new call to 'disk_media_changed' is not allowed until this
53
; function is called.
54
; Optional, may be NULL (if media is not removable).
55
; void closemedia(void* userdata);
2381 hidnplayr 56
        querymedia      dd ?
2288 clevermous 57
; The pointer to the function which determines capabilities of the media.
58
; int querymedia(void* userdata, DISKMEDIAINFO* info);
59
; Return value: one of DISK_STATUS_*
2381 hidnplayr 60
        read            dd ?
2288 clevermous 61
; The pointer to the function which reads data from the device.
62
; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors);
63
; input: *numsectors = number of sectors to read
64
; output: *numsectors = number of sectors which were successfully read
65
; Return value: one of DISK_STATUS_*
2381 hidnplayr 66
        write           dd ?
2288 clevermous 67
; The pointer to the function which writes data to the device.
68
; Optional, may be NULL.
69
; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors);
70
; input: *numsectors = number of sectors to write
71
; output: *numsectors = number of sectors which were successfully written
72
; Return value: one of DISK_STATUS_*
2381 hidnplayr 73
        flush           dd ?
2288 clevermous 74
; The pointer to the function which flushes the internal device cache.
75
; Optional, may be NULL.
76
; int flush(void* userdata);
77
; Return value: one of DISK_STATUS_*
78
; Note that read/write are called by the cache manager, so a driver should not
79
; create a software cache. This function is implemented for flushing a hardware
80
; cache, if it exists.
2381 hidnplayr 81
        adjust_cache_size       dd ?
2288 clevermous 82
; The pointer to the function which returns the cache size for this device.
83
; Optional, may be NULL.
7727 dunkaist 84
; unsigned int adjust_cache_size(void* userdata, unsigned int suggested_size);
2288 clevermous 85
; Return value: 0 = disable cache, otherwise = used cache size in bytes.
10053 Doczom 86
        LoadTray        dd ?
87
; This pointer to the function which load and unload tray drive.
88
; Optional, may be NULL
89
; int LoadTray(void* userdata, int flags);
90
; flags:
91
; 0 - load
92
; 1 - unload
93
; Return value: one of DISK_STATUS_*
2288 clevermous 94
ends
95
 
96
; This structure holds information on a medium.
97
; Objects with this structure are allocated by the kernel as a part of the DISK
98
; structure and are filled by a driver in the 'querymedia' callback.
2381 hidnplayr 99
struct  DISKMEDIAINFO
100
        Flags           dd ?
2288 clevermous 101
; Combination of DISK_MEDIA_* bits.
2381 hidnplayr 102
        SectorSize      dd ?
2288 clevermous 103
; Size of the sector.
2381 hidnplayr 104
        Capacity        dq ?
2288 clevermous 105
; Size of the media in sectors.
10053 Doczom 106
        LastSessionSector       dd ?
107
; Number last session sectors for CDFS
2288 clevermous 108
ends
109
 
110
; This structure represents the disk cache. To follow the old implementation,
111
; there are two distinct caches for a disk, one for "system" data,and the other
112
; for "application" data.
2381 hidnplayr 113
struct  DISKCACHE
2288 clevermous 114
; The following fields are inherited from data32.inc:cache_ideX.
2381 hidnplayr 115
        pointer         dd ?
116
        data_size       dd ?    ; unused
117
        data            dd ?
118
        sad_size        dd ?
119
        search_start    dd ?
5089 clevermous 120
        sector_size_log dd ?
2288 clevermous 121
ends
122
 
123
; This structure represents a disk device and its media for the kernel.
124
; This structure is allocated by the kernel in the 'disk_add' function,
125
; freed in the 'disk_dereference' function.
2381 hidnplayr 126
struct  DISK
2288 clevermous 127
; Fields of disk object
2381 hidnplayr 128
        Next            dd ?
129
        Prev            dd ?
2288 clevermous 130
; All disk devices are linked in one list with these two fields.
131
; Head of the list is the 'disk_list' variable.
2381 hidnplayr 132
        Functions       dd ?
2288 clevermous 133
; Pointer to the 'DISKFUNC' structure with driver functions.
2381 hidnplayr 134
        Name            dd ?
2288 clevermous 135
; Pointer to the string used for accesses through the global filesystem.
2381 hidnplayr 136
        UserData        dd ?
2288 clevermous 137
; This field is passed to all callback functions so a driver can decide which
138
; physical device is addressed.
2381 hidnplayr 139
        DriverFlags     dd ?
2288 clevermous 140
; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined.
141
; If it is set, the driver will never issue 'disk_media_changed' notification
142
; with argument set to true, so the kernel must try to detect media during
143
; requests from the file system.
2381 hidnplayr 144
        RefCount        dd ?
2288 clevermous 145
; Count of active references to this structure. One reference is kept during
146
; the lifetime of the structure between 'disk_add' and 'disk_del'.
147
; Another reference is taken during any filesystem operation for this disk.
148
; One reference is added if media is inserted.
149
; The structure is destroyed when the reference count decrements to zero:
150
; this usually occurs in 'disk_del', but can be delayed to the end of last
151
; filesystem operation, if one is active.
2381 hidnplayr 152
        MediaLock       MUTEX
2288 clevermous 153
; Lock to protect the MEDIA structure. See the description after
154
; 'disk_list_mutex' for the locking strategy.
155
; Fields of media object
2381 hidnplayr 156
        MediaInserted   db ?
2288 clevermous 157
; 0 if media is not inserted, nonzero otherwise.
2381 hidnplayr 158
        MediaUsed       db ?
2288 clevermous 159
; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is
160
; nonzero, this field is nonzero too; however, when .MediaRefCount goes
161
; to zero, there is some time interval during which media object is still used.
3460 clevermous 162
                        dw ? ; padding
2288 clevermous 163
; The following fields are not valid unless either .MediaInserted is nonzero
164
; or they are accessed from a code which has obtained the reference when
165
; .MediaInserted was nonzero.
2381 hidnplayr 166
        MediaRefCount   dd ?
2288 clevermous 167
; Count of active references to the media object. One reference is kept during
168
; the lifetime of the media between two calls to 'disk_media_changed'.
169
; Another reference is taken during any filesystem operation for this media.
170
; The callback 'closemedia' is called when the reference count decrements to
171
; zero: this usually occurs in 'disk_media_changed', but can be delayed to the
172
; end of the last filesystem operation, if one is active.
2381 hidnplayr 173
        MediaInfo       DISKMEDIAINFO
2288 clevermous 174
; This field keeps information on the current media.
2381 hidnplayr 175
        NumPartitions   dd ?
2288 clevermous 176
; Number of partitions on this media.
2381 hidnplayr 177
        Partitions      dd ?
2288 clevermous 178
; Pointer to array of .NumPartitions pointers to PARTITION structures.
2381 hidnplayr 179
        cache_size      dd ?
2288 clevermous 180
; inherited from cache_ideX_size
4437 clevermous 181
        CacheLock       MUTEX
182
; Lock to protect both caches.
2381 hidnplayr 183
        SysCache        DISKCACHE
184
        AppCache        DISKCACHE
2288 clevermous 185
; Two caches for the disk.
186
ends
187
 
188
; This structure represents one partition for the kernel. This is a base
189
; template, the actual contents after common fields is determined by the
190
; file system code for this partition.
2381 hidnplayr 191
struct  PARTITION
192
        FirstSector     dq ?
2288 clevermous 193
; First sector of the partition.
2381 hidnplayr 194
        Length          dq ?
2288 clevermous 195
; Length of the partition in sectors.
2381 hidnplayr 196
        Disk            dd ?
2288 clevermous 197
; Pointer to parent DISK structure.
2381 hidnplayr 198
        FSUserFunctions dd ?
2288 clevermous 199
; Handlers for the sysfunction 70h. This field is a pointer to the following
3742 clevermous 200
; array. The first dword is pointer to disconnect handler.
201
; The first dword is a number of supported subfunctions, other dwords
2288 clevermous 202
; point to handlers of corresponding subfunctions.
203
; ...fs-specific data may follow...
204
ends
205
 
206
; This is an external structure, it represents an entry in the partition table.
2381 hidnplayr 207
struct  PARTITION_TABLE_ENTRY
208
        Bootable        db ?
2288 clevermous 209
; 80h = bootable partition, 0 = non-bootable partition, other values = invalid
2381 hidnplayr 210
        FirstHead       db ?
211
        FirstSector     db ?
212
        FirstTrack      db ?
2288 clevermous 213
; Coordinates of first sector in CHS.
2381 hidnplayr 214
        Type            db ?
2288 clevermous 215
; Partition type, one of predefined constants. 0 = empty, several types denote
216
; extended partition (see process_partition_table_entry), we are not interested
217
; in other values.
2381 hidnplayr 218
        LastHead        db ?
219
        LastSector      db ?
220
        LastTrack       db ?
2288 clevermous 221
; Coordinates of last sector in CHS.
2381 hidnplayr 222
        FirstAbsSector  dd ?
2288 clevermous 223
; Coordinate of first sector in LBA.
2381 hidnplayr 224
        Length          dd ?
2288 clevermous 225
; Length of the partition in sectors.
226
ends
227
 
6827 dunkaist 228
; GUID Partition Table Header, UEFI 2.6, Table 18
229
struct GPTH
230
        Signature                rb 8
231
; 'EFI PART'
232
        Revision                 dd ?
233
; 0x00010000
234
        HeaderSize               dd ?
235
; Size of this header in bytes, must fit to one sector.
236
        HeaderCRC32              dd ?
237
; Set this field to zero, compute CRC32 via 0xEDB88320, compare.
238
        Reserved                 dd ?
6828 dunkaist 239
; Must be zero.
6827 dunkaist 240
        MyLBA                    dq ?
241
; LBA of the sector containing this GPT header.
242
        AlternateLBA             dq ?
243
; LBA of the sector containing the other GPT header.
244
; AlternateLBA of Primary GPTH points to Backup one and vice versa.
245
        FirstUsableLBA           dq ?
246
; Only sectors between first and last UsableLBA may form partitions
247
        LastUsableLBA            dq ?
248
        DiskGUID                 rb 16
249
; Globally Unique IDentifier
250
        PartitionEntryLBA        dq ?
251
; First LBA of Partition Entry Array.
252
; Length in bytes is computed as a product of two following fields.
253
        NumberOfPartitionEntries dd ?
254
; Actual number of partitions depends on the contents of Partition Entry Array.
255
; A partition entry is unused if zeroed.
256
        SizeOfPartitionEntry     dd ?   ; in bytes
257
        PartitionEntryArrayCRC32 dd ?
258
; Same CRC as for GPT header.
259
ends
260
 
261
; GPT Partition Entry, UEFI 2.6, Table 19
262
struct GPE
263
        PartitionTypeGUID       rb 16
264
        UniquePartitionGUID     rb 16
265
        StartingLBA             dq ?
266
        EndingLBA               dq ?
267
; Length in sectors is EndingLBA - StartingLBA + 1.
268
        Attributes              dq ?
269
        PartitionName           rb 72
270
ends
271
 
2288 clevermous 272
; =============================================================================
273
; ================================ Global data ================================
274
; =============================================================================
275
iglobal
276
; The pseudo-item for the list of all DISK structures.
277
; Initialized to the empty list.
278
disk_list:
279
        dd      disk_list
280
        dd      disk_list
281
endg
282
uglobal
283
; This mutex guards all operations with the global list of DISK structures.
284
disk_list_mutex MUTEX
285
; * There are two dependent objects, a disk and a media. In the simplest case,
286
;   disk and media are both non-removable. However, in the general case both
287
;   can be removed at any time, simultaneously or only media,and this makes things
288
;   complicated.
289
; * For efficiency, both disk and media objects are located in the one
290
;   structure named DISK. However, logically they are different.
291
; * The following operations use data of disk object: adding (disk_add);
292
;   deleting (disk_del); filesystem (fs_lfn which eventually calls
293
;   dyndisk_handler or dyndisk_enum_root).
294
; * The following operations use data of media object: adding/removing
295
;   (disk_media_changed); filesystem (fs_lfn which eventually calls
296
;   dyndisk_handler; dyndisk_enum_root doesn't work with media).
297
; * Notifications disk_add, disk_media_changed, disk_del are synchronized
298
;   between themselves, this is a requirement for the driver. However, file
299
;   system operations are asynchronous, can be issued at any time by any
300
;   thread.
301
; * We must prevent a situation when a filesystem operation thinks that the
302
;   object is still valid but in fact the notification has destroyed the
303
;   object. So we keep a reference counter for both disk and media and destroy
304
;   the object when this counter goes to zero.
305
; * The driver must know when it is safe to free driver-allocated resources.
306
;   The object can be alive even after death notification has completed.
307
;   We use special callbacks to satisfy both assertions: 'close' for the disk
308
;   and 'closemedia' for the media. The destruction of the object includes
309
;   calling the corresponding callback.
310
; * Each filesystem operation keeps one reference for the disk and one
311
;   reference for the media. Notification disk_del forces notification on the
312
;   media death, so the reference counter for the disk is always not less than
313
;   the reference counter for the media.
314
; * Two operations "get the object" and "increment the reference counter" can
315
;   not be done simultaneously. We use a mutex to guard the consistency here.
316
;   It must be a part of the container for the object, so that this mutex can
317
;   be acquired as a part of getting the object from the container. The
318
;   container for disk object is the global list, and this list is guarded by
319
;   'disk_list_mutex'. The container for media object is the disk object, and
320
;   the corresponding mutex is DISK.MediaLock.
321
; * Notifications do not change the data of objects, they can only remove
322
;   objects. Thus we don't need another synchronization at this level. If two
323
;   filesystem operations are referencing the same filesystem data, this is
324
;   better resolved at the level of the filesystem.
325
endg
326
 
327
iglobal
5089 clevermous 328
; The function 'disk_scan_partitions' needs three sector-sized buffers for
2288 clevermous 329
; MBR, bootsector and fs-temporary sector data. It can not use the static
330
; buffers always, since it can be called for two or more disks in parallel.
331
; However, this case is not typical. We reserve three static 512-byte buffers
332
; and a flag that these buffers are currently used. If 'disk_scan_partitions'
333
; detects that the buffers are currently used, it allocates buffers from the
5089 clevermous 334
; heap. Also, the heap is used when sector size is other than 512.
2288 clevermous 335
; The flag is implemented as a global dword variable. When the static buffers
336
; are not used, the value is -1. When the static buffers are used, the value
337
; is normally 0 and temporarily can become greater. The function increments
338
; this value. If the resulting value is zero, it uses the buffers and
339
; decrements the value when the job is done. Otherwise, it immediately
340
; decrements the value and uses buffers from the heap, allocated in the
341
; beginning and freed in the end.
342
partition_buffer_users  dd      -1
343
endg
344
uglobal
345
; The static buffers for MBR, bootsector and fs-temporary sector data.
346
align 16
347
mbr_buffer      rb      512
348
bootsect_buffer rb      512
349
fs_tmp_buffer   rb      512
350
endg
351
 
352
iglobal
353
; This is the array of default implementations of driver callbacks.
354
; Same as DRIVERFUNC structure except for the first field; all functions must
355
; have the default implementations.
356
align 4
357
disk_default_callbacks:
358
        dd      disk_default_close
359
        dd      disk_default_closemedia
360
        dd      disk_default_querymedia
361
        dd      disk_default_read
362
        dd      disk_default_write
363
        dd      disk_default_flush
364
        dd      disk_default_adjust_cache_size
365
endg
366
 
367
; =============================================================================
368
; ================================= Functions =================================
369
; =============================================================================
370
 
371
; This function registers a disk device.
372
; This includes:
373
; - allocating an internal structure describing this device;
374
; - registering this structure in the global filesystem.
375
; The function initializes the disk as if there is no media. If a media is
376
; present, the function 'disk_media_changed' should be called after this
377
; function succeeds.
378
; Parameters:
379
; [esp+4] = pointer to DISKFUNC structure with the callbacks
380
; [esp+8] = pointer to name (ASCIIZ string)
381
; [esp+12] = userdata to be passed to the callbacks as is.
382
; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit
383
;            is defined.
384
; Return value:
385
; NULL = operation has failed
386
; non-NULL = handle of the disk. This handle can be used
387
; in the operations with other Disk* functions.
388
; The handle is the pointer to the internal structure DISK.
389
disk_add:
390
        push    ebx esi         ; save used registers to be stdcall
391
; 1. Allocate the DISK structure.
392
; 1a. Call the heap manager.
3598 clevermous 393
        movi    eax, sizeof.DISK
2288 clevermous 394
        call    malloc
395
; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0.
396
        test    eax, eax
397
        jz      .nothing
398
; 2. Copy the disk name to the DISK structure.
399
; 2a. Get length of the name, including the terminating zero.
400
        mov     ebx, [esp+8+8]  ; ebx = pointer to name
401
        push    eax             ; save allocated pointer to DISK
402
        xor     eax, eax        ; the argument of malloc() is in eax
403
@@:
404
        inc     eax
405
        cmp     byte [ebx+eax-1], 0
406
        jnz     @b
3681 clevermous 407
; 2b. Call the heap manager.
2288 clevermous 408
        call    malloc
409
; 2c. Check the result. If allocation failed, go to 7.
410
        pop     esi             ; restore allocated pointer to DISK
411
        test    eax, eax
412
        jz      .free
413
; 2d. Store the allocated pointer to the DISK structure.
414
        mov     [esi+DISK.Name], eax
415
; 2e. Copy the name.
416
@@:
417
        mov     dl, [ebx]
418
        mov     [eax], dl
419
        inc     ebx
420
        inc     eax
421
        test    dl, dl
422
        jnz     @b
423
; 3. Copy other arguments of the function to the DISK structure.
424
        mov     eax, [esp+4+8]
425
        mov     [esi+DISK.Functions], eax
426
        mov     eax, [esp+12+8]
427
        mov     [esi+DISK.UserData], eax
428
        mov     eax, [esp+16+8]
429
        mov     [esi+DISK.DriverFlags], eax
430
; 4. Initialize other fields of the DISK structure.
431
; Media is not inserted, reference counter is 1.
432
        lea     ecx, [esi+DISK.MediaLock]
433
        call    mutex_init
434
        xor     eax, eax
435
        mov     dword [esi+DISK.MediaInserted], eax
3460 clevermous 436
        mov     [esi+DISK.MediaRefCount], eax
2288 clevermous 437
        inc     eax
438
        mov     [esi+DISK.RefCount], eax
439
; The DISK structure is initialized.
440
; 5. Insert the new structure to the global list.
441
; 5a. Acquire the mutex.
442
        mov     ecx, disk_list_mutex
443
        call    mutex_lock
444
; 5b. Insert item to the tail of double-linked list.
445
        mov     edx, disk_list
446
        list_add_tail esi, edx     ;esi= new edx= list head
447
; 5c. Release the mutex.
448
        call    mutex_unlock
449
; 6. Return with eax = pointer to DISK.
450
        xchg    eax, esi
451
        jmp     .nothing
452
.free:
453
; Memory allocation for DISK structure succeeded, but for disk name failed.
454
; 7. Free the DISK structure.
455
        xchg    eax, esi
456
        call    free
457
; 8. Return with eax = 0.
458
        xor     eax, eax
459
.nothing:
460
; 9. Return.
461
        pop     esi ebx         ; restore used registers to be stdcall
462
        ret     16              ; purge 4 dword arguments to be stdcall
463
 
464
; This function deletes a disk device from the global filesystem.
465
; This includes:
466
; - removing a media including all partitions;
467
; - deleting this structure from the global filesystem;
468
; - dereferencing the DISK structure and possibly destroying it.
469
; Parameters:
470
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
471
; Return value: none.
472
disk_del:
473
        push    esi         ; save used registers to be stdcall
474
; 1. Force media to be removed. If the media is already removed, the
475
; call does nothing.
2643 clevermous 476
        mov     esi, [esp+4+4]  ; esi = handle of the disk
2288 clevermous 477
        stdcall disk_media_changed, esi, 0
478
; 2. Delete the structure from the global list.
479
; 2a. Acquire the mutex.
480
        mov     ecx, disk_list_mutex
481
        call    mutex_lock
482
; 2b. Delete item from double-linked list.
483
        mov     eax, [esi+DISK.Next]
484
        mov     edx, [esi+DISK.Prev]
485
        mov     [eax+DISK.Prev], edx
486
        mov     [edx+DISK.Next], eax
487
; 2c. Release the mutex.
488
        call    mutex_unlock
489
; 3. The structure still has one reference created in disk_add. Remove this
490
; reference. If there are no other references, disk_dereference will free the
491
; structure.
492
        call    disk_dereference
493
; 4. Return.
494
        pop     esi             ; restore used registers to be stdcall
495
        ret     4               ; purge 1 dword argument to be stdcall
496
 
497
; This is an internal function which removes a previously obtained reference
498
; to the disk. If this is the last reference, this function lets the driver
499
; finalize all associated data, and afterwards frees the DISK structure.
500
; esi = pointer to DISK structure
501
disk_dereference:
502
; 1. Decrement reference counter. Use atomic operation to correctly handle
503
; possible simultaneous calls.
504
        lock dec [esi+DISK.RefCount]
505
; 2. If the result is nonzero, there are other references, so nothing to do.
506
; In this case, return (go to 4).
507
        jnz     .nothing
508
; 3. If we are here, we just removed the last reference and must destroy the
509
; disk object.
510
; 3a. Call the driver.
511
        mov     al, DISKFUNC.close
512
        stdcall disk_call_driver
513
; 3b. Free the structure.
514
        xchg    eax, esi
3202 clevermous 515
        push    ebx
2288 clevermous 516
        call    free
3202 clevermous 517
        pop     ebx
2288 clevermous 518
; 4. Return.
519
.nothing:
520
        ret
521
 
522
; This is an internal function which removes a previously obtained reference
523
; to the media. If this is the last reference, this function calls 'closemedia'
524
; callback to signal the driver that the processing has finished and it is safe
525
; to inform about a new media.
526
; esi = pointer to DISK structure
527
disk_media_dereference:
528
; 1. Decrement reference counter. Use atomic operation to correctly handle
529
; possible simultaneous calls.
530
        lock dec [esi+DISK.MediaRefCount]
531
; 2. If the result is nonzero, there are other references, so nothing to do.
532
; In this case, return (go to 4).
533
        jnz     .nothing
534
; 3. If we are here, we just removed the last reference and must destroy the
535
; media object.
536
; Note that the same place inside the DISK structure is reused for all media
537
; objects, so we must guarantee that reusing does not happen while freeing.
538
; Reusing is only possible when someone processes a new media. There are two
539
; mutually exclusive variants:
540
; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit
541
;   in DISK.DriverFlags is not set). In this case, we require from the driver
542
;   that such notification (except for the first one) can occur only after a
543
;   call to 'closemedia' callback.
544
; * driver does not issue media insert notifications. In this case, the kernel
545
;   itself must sometimes check whether media is inserted. We have the flag
546
;   DISK.MediaUsed, visible to the kernel. This flag signals to the other parts
547
;   of kernel that the way is free.
548
; In the first case other parts of the kernel do not use DISK.MediaUsed, so it
549
; does not matter when this flag is cleared. In the second case this flag must
550
; be cleared after all other actions, including call to 'closemedia'.
551
; 3a. Free all partitions.
552
        push    esi edi
553
        mov     edi, [esi+DISK.NumPartitions]
554
        mov     esi, [esi+DISK.Partitions]
555
        test    edi, edi
556
        jz      .nofree
557
.freeloop:
558
        lodsd
3742 clevermous 559
        mov     ecx, [eax+PARTITION.FSUserFunctions]
560
        call    dword [ecx]
2288 clevermous 561
        dec     edi
562
        jnz     .freeloop
563
.nofree:
564
        pop     edi esi
565
; 3b. Free the cache.
566
        call    disk_free_cache
567
; 3c. Call the driver.
568
        mov     al, DISKFUNC.closemedia
569
        stdcall disk_call_driver
570
; 3d. Clear the flag.
571
        mov     [esi+DISK.MediaUsed], 0
572
.nothing:
573
        ret
574
 
575
; This function is called by the driver and informs the kernel that the media
576
; has changed. If the media is non-removable, it is called exactly once
577
; immediately after 'disk_add' and once from 'disk_del'.
578
; Parameters:
579
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
580
; [esp+8] = new status of the media: zero = no media, nonzero = media inserted.
581
disk_media_changed:
582
        push    ebx esi edi             ; save used registers to be stdcall
583
; 1. Remove the existing media, if it is present.
584
        mov     esi, [esp+4+12]         ; esi = pointer to DISK
585
; 1a. Check whether it is present. Since DISK.MediaInserted is changed only
586
; in this function and calls to this function are synchronized, no lock is
587
; required for checking.
588
        cmp     [esi+DISK.MediaInserted], 0
589
        jz      .noremove
590
; We really need to remove the media.
591
; 1b. Acquire mutex.
592
        lea     ecx, [esi+DISK.MediaLock]
593
        call    mutex_lock
594
; 1c. Clear the flag.
595
        mov     [esi+DISK.MediaInserted], 0
596
; 1d. Release mutex.
597
        call    mutex_unlock
598
; 1e. Remove the "lifetime" reference and possibly destroy the structure.
599
        call    disk_media_dereference
600
.noremove:
601
; 2. Test whether there is new media.
602
        cmp     dword [esp+8+12], 0
603
        jz      .noinsert
604
; Yep, there is.
605
; 3. Process the new media. We assume that all media fields are available to
606
; use, see comments in 'disk_media_dereference' (this covers using by previous
607
; media referencers) and note that calls to this function are synchronized
608
; (this covers using by new media referencers).
609
; 3a. Call the 'querymedia' callback.
610
; .Flags are set to zero for possible future extensions.
611
        lea     edx, [esi+DISK.MediaInfo]
10053 Doczom 612
        and     [edx+DISKMEDIAINFO.LastSessionSector], 0
2288 clevermous 613
        and     [edx+DISKMEDIAINFO.Flags], 0
614
        mov     al, DISKFUNC.querymedia
615
        stdcall disk_call_driver, edx
616
; 3b. Check the result of the callback. Abort if it failed.
617
        test    eax, eax
618
        jnz     .noinsert
619
; 3c. Allocate the cache unless disabled by the driver. Abort if failed.
620
        call    disk_init_cache
621
        test    al, al
622
        jz      .noinsert
623
; 3d. Acquire the lifetime reference for the media object.
624
        inc     [esi+DISK.MediaRefCount]
625
; 3e. Scan for partitions. Ignore result; the list of partitions is valid even
626
; on errors.
627
        call    disk_scan_partitions
628
; 3f. Media is inserted and available for use.
629
        inc     [esi+DISK.MediaInserted]
630
.noinsert:
631
; 4. Return.
632
        pop     edi esi ebx             ; restore used registers to be stdcall
633
        ret     8                       ; purge 2 dword arguments to be stdcall
634
 
635
; This function is a thunk for all functions of a disk driver.
636
; It checks whether the referenced function is implemented in the driver.
637
; If so, this function jumps to the function in the driver.
638
; Otherwise, it jumps to the default implementation.
639
; al = offset of function in the DISKFUNC structure;
640
; esi = pointer to the DISK structure;
641
; stack is the same as for the corresponding function except that the
642
; first parameter (void* userdata) is prepended automatically.
643
disk_call_driver:
644
        movzx   eax, al ; eax = offset of function in the DISKFUNC structure
645
; 1. Prepend the first argument to the stack.
646
        pop     ecx     ; ecx = return address
647
        push    [esi+DISK.UserData]     ; add argument
648
        push    ecx     ; save return address
649
; 2. Check that the required function is inside the table. If not, go to 5.
650
        mov     ecx, [esi+DISK.Functions]
651
        cmp     eax, [ecx+DISKFUNC.strucsize]
652
        jae     .default
653
; 3. Check that the required function is implemented. If not, go to 5.
654
        mov     ecx, [ecx+eax]
655
        test    ecx, ecx
656
        jz      .default
657
; 4. Jump to the required function.
658
        jmp     ecx
659
.default:
660
; 5. Driver does not implement the required function; use default implementation.
661
        jmp     dword [disk_default_callbacks+eax-4]
662
 
663
; The default implementation of DISKFUNC.querymedia.
664
disk_default_querymedia:
3598 clevermous 665
        movi    eax, DISK_STATUS_INVALID_CALL
2288 clevermous 666
        ret     8
667
 
668
; The default implementation of DISKFUNC.read and DISKFUNC.write.
669
disk_default_read:
670
disk_default_write:
3598 clevermous 671
        movi    eax, DISK_STATUS_INVALID_CALL
2288 clevermous 672
        ret     20
673
 
674
; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and
675
; DISKFUNC.flush.
676
disk_default_close:
677
disk_default_closemedia:
678
disk_default_flush:
679
        xor     eax, eax
680
        ret     4
681
 
682
; The default implementation of DISKFUNC.adjust_cache_size.
683
disk_default_adjust_cache_size:
3164 clevermous 684
        mov     eax, [esp+8]
685
        ret     8
2288 clevermous 686
 
687
; This is an internal function called from 'disk_media_changed' when a new media
688
; is detected. It creates the list of partitions for the media.
689
; If media is not partitioned, then the list consists of one partition which
690
; covers all the media.
691
; esi = pointer to the DISK structure.
692
disk_scan_partitions:
693
; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list.
694
        and     [esi+DISK.NumPartitions], 0
695
        and     [esi+DISK.Partitions], 0
5089 clevermous 696
; 2. Acquire the buffer for MBR and bootsector tests. See the comment before
2288 clevermous 697
; the 'partition_buffer_users' variable.
5089 clevermous 698
        mov     eax, [esi+DISK.MediaInfo.SectorSize]
699
        cmp     eax, 512
700
        jnz     @f
2288 clevermous 701
        mov     ebx, mbr_buffer         ; assume the global buffer is free
702
        lock inc [partition_buffer_users]
703
        jz      .buffer_acquired        ; yes, it is free
704
        lock dec [partition_buffer_users]       ; no, we must allocate
5089 clevermous 705
@@:
706
        lea     eax, [eax*3]
707
        stdcall kernel_alloc, eax
2288 clevermous 708
        test    eax, eax
709
        jz      .nothing
710
        xchg    eax, ebx
711
.buffer_acquired:
712
; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no
713
; more than MAX_NUM_PARTITION times.
5089 clevermous 714
; 3. Prepare things for the loop.
2288 clevermous 715
; ebp will hold the sector number for current MBR/EBR.
716
; [esp] will hold the sector number for current extended partition, if there
717
; is one.
718
; [esp+4] will hold the counter that prevents long loops.
719
        push    ebp             ; save ebp
720
        push    MAX_NUM_PARTITIONS      ; the counter of max MBRs to process
721
        xor     ebp, ebp        ; start from sector zero
722
        push    ebp             ; no extended partition yet
5089 clevermous 723
; 4. MBR is 512 bytes long. If sector size is less than 512 bytes,
6827 dunkaist 724
; assume no MBR, no partitions and go to 11.
5089 clevermous 725
        cmp     [esi+DISK.MediaInfo.SectorSize], 512
726
        jb      .notmbr
2288 clevermous 727
.new_mbr:
728
; 5. Read the current sector.
729
; Note that 'read' callback operates with 64-bit sector numbers, so we must
730
; push additional zero as a high dword of sector number.
731
        mov     al, DISKFUNC.read
732
        push    1
733
        stdcall disk_call_driver, ebx, ebp, 0, esp
734
        pop     ecx
735
; 6. If the read has failed, abort the loop.
736
        dec     ecx
737
        jnz     .mbr_failed
738
; 7. Check the MBR/EBR signature. If it is wrong, abort the loop.
739
; Soon we will access the partition table which starts at ebx+0x1BE,
740
; so we can fill its address right now. If we do it now, then the addressing
741
; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset.
742
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
743
        cmp     word [ecx+0x40], 0xaa55
9941 Doczom 744
        jnz     .notmbr
2288 clevermous 745
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to
6827 dunkaist 746
; execute step 10 and possibly step 11.
2288 clevermous 747
        test    ebp, ebp
748
        jnz     .mbr
6827 dunkaist 749
; 9. Handle GUID Partition Table
750
; 9a. Check if MBR is protective
751
        call    is_protective_mbr
752
        jnz     .no_gpt
753
; 9b. If so, try to scan GPT headers
754
        call    disk_scan_gpt
755
; 9c. If any GPT header is valid, ignore MBR
756
        jz      .done
757
; Otherwise process legacy/protective MBR
758
.no_gpt:
2288 clevermous 759
; The partition table can be present or not present. In the first case, we just
760
; read the MBR. In the second case, we just read the bootsector for a
761
; filesystem.
762
; The following algorithm is used to distinguish between these cases.
763
; A. If at least one entry of the partition table is invalid, this is
764
;    a bootsector. See the description of 'is_partition_table_entry' for
765
;    definition of validity.
766
; B. If all entries are empty (filesystem type field is zero) and the first
767
;    byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to
768
;    have zeros in the place of partition table.
769
; C. Otherwise, this is an MBR.
6827 dunkaist 770
; 10. Test for MBR vs bootsector.
771
; 10a. Check entries. If any is invalid, go to 11 (rule A).
2288 clevermous 772
        call    is_partition_table_entry
773
        jc      .notmbr
774
        add     ecx, 10h
775
        call    is_partition_table_entry
776
        jc      .notmbr
777
        add     ecx, 10h
778
        call    is_partition_table_entry
779
        jc      .notmbr
780
        add     ecx, 10h
781
        call    is_partition_table_entry
782
        jc      .notmbr
6827 dunkaist 783
; 10b. Check types of the entries. If at least one is nonzero, go to 12 (rule C).
2288 clevermous 784
        mov     al, [ecx-30h+PARTITION_TABLE_ENTRY.Type]
785
        or      al, [ecx-20h+PARTITION_TABLE_ENTRY.Type]
786
        or      al, [ecx-10h+PARTITION_TABLE_ENTRY.Type]
787
        or      al, [ecx+PARTITION_TABLE_ENTRY.Type]
788
        jnz     .mbr
6827 dunkaist 789
; 10c. Empty partition table or bootsector with many zeroes? (rule B)
2288 clevermous 790
        cmp     byte [ebx], 0EBh
791
        jz      .notmbr
792
        cmp     byte [ebx], 0E9h
793
        jnz     .mbr
794
.notmbr:
6827 dunkaist 795
; 11. This is not an  MBR. The media is not partitioned. Create one partition
2288 clevermous 796
; which covers all the media and abort the loop.
797
        stdcall disk_add_partition, 0, 0, \
3742 clevermous 798
                dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4], esi
2288 clevermous 799
        jmp     .done
800
.mbr:
6827 dunkaist 801
; 12. Process all entries of the new MBR/EBR
2288 clevermous 802
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
803
        push    0       ; assume no extended partition
804
        call    process_partition_table_entry
805
        add     ecx, 10h
806
        call    process_partition_table_entry
807
        add     ecx, 10h
808
        call    process_partition_table_entry
809
        add     ecx, 10h
810
        call    process_partition_table_entry
811
        pop     ebp
6827 dunkaist 812
; 13. Test whether we found a new EBR and should continue the loop.
813
; 13a. If there was no next EBR, return.
2288 clevermous 814
        test    ebp, ebp
815
        jz      .done
816
; Ok, we have EBR.
6827 dunkaist 817
; 13b. EBRs addresses are relative to the start of extended partition.
2288 clevermous 818
; For simplicity, just abort if an 32-bit overflow occurs; large disks
819
; are most likely partitioned with GPT, not MBR scheme, since the precise
820
; calculation here would increase limit just twice at the price of big
821
; compatibility problems.
822
        pop     eax     ; load extended partition
823
        add     ebp, eax
824
        jc      .mbr_failed
6827 dunkaist 825
; 13c. If extended partition has not yet started, start it.
2288 clevermous 826
        test    eax, eax
827
        jnz     @f
828
        mov     eax, ebp
829
@@:
6827 dunkaist 830
; 13d. If the limit is not exceeded, continue the loop.
2288 clevermous 831
        dec     dword [esp]
832
        push    eax     ; store extended partition
833
        jnz     .new_mbr
834
.mbr_failed:
835
.done:
6827 dunkaist 836
; 14. Cleanup after the loop.
2288 clevermous 837
        pop     eax     ; not important anymore
838
        pop     eax     ; not important anymore
839
        pop     ebp     ; restore ebp
6827 dunkaist 840
; 15. Release the buffer.
841
; 15a. Test whether it is the global buffer or we have allocated it.
2288 clevermous 842
        cmp     ebx, mbr_buffer
843
        jz      .release_partition_buffer
6827 dunkaist 844
; 15b. If we have allocated it, free it.
2288 clevermous 845
        xchg    eax, ebx
846
        call    free
847
        jmp     .nothing
6827 dunkaist 848
; 15c. Otherwise, release reference.
2288 clevermous 849
.release_partition_buffer:
850
        lock dec [partition_buffer_users]
851
.nothing:
6827 dunkaist 852
; 16. Return.
2288 clevermous 853
        ret
854
 
6827 dunkaist 855
 
856
; This function is called from disk_scan_partitions to validate and parse
857
; primary and backup GPTs.
858
proc disk_scan_gpt
6828 dunkaist 859
        push    ecx
6827 dunkaist 860
; Scan primary GPT (second sector)
861
        stdcall scan_gpt, 1, 0
862
        test    eax, eax
863
; There is no code to restore backup GPT if it's corrupt.
864
; Therefore just exit if Primary GPT has been parsed successfully.
865
        jz      .exit
866
        DEBUGF  1, 'K : Primary GPT is corrupt, trying backup one\n'
867
        mov     eax, dword[esi+DISK.MediaInfo.Capacity+0]
868
        mov     edx, dword[esi+DISK.MediaInfo.Capacity+4]
869
        sub     eax, 1
870
        sbb     edx, 0
871
; Scan backup GPT (last sector)
872
        stdcall scan_gpt, eax, edx
873
        test    eax, eax
874
        jz      .exit
875
        DEBUGF  1, 'K : Backup GPT is also corrupt, fallback to legacy MBR\n'
876
.exit:
877
; Return value is ZF
6828 dunkaist 878
        pop     ecx
6827 dunkaist 879
        ret
880
endp
881
 
882
 
883
; This function is called from disk_scan_gpt to process a single GPT.
884
proc scan_gpt _mylba:qword
885
locals
886
        GPEA_len dd ?   ; Length of GPT Partition Entry Array in bytes
887
endl
888
        push    ebx edi
889
; Allocalte memory for GPT header
890
        mov     eax, [esi+DISK.MediaInfo.SectorSize]
891
        stdcall kernel_alloc, eax
892
        test    eax, eax
893
        jz      .fail
894
; Save pointer to stack, just in case
895
        push    eax
896
        mov     ebx, eax
897
; Read GPT header
898
        mov     al, DISKFUNC.read
899
        push    1
900
        stdcall disk_call_driver, ebx, dword[_mylba+0], dword[_mylba+4], esp
901
        pop     ecx
902
        test    eax, eax
903
        jnz     .fail_free_gpt
904
; Check signature
905
        cmp     dword[ebx+GPTH.Signature+0], 'EFI '
906
        jnz     .fail_free_gpt
907
        cmp     dword[ebx+GPTH.Signature+4], 'PART'
908
        jnz     .fail_free_gpt
909
; Check Revision
910
        cmp     [ebx+GPTH.Revision], 0x00010000
911
        jnz     .fail_free_gpt
912
; Compute and check CRC32
913
        xor     edx, edx
914
        xchg    edx, [ebx+GPTH.HeaderCRC32]
915
        mov     eax, -1
916
        stdcall crc_32, 0xEDB88320, ebx, [ebx+GPTH.HeaderSize]
917
        xor     eax, -1
918
        cmp     eax, edx
919
        jnz     .fail_free_gpt
920
; Reserved must be zero
921
        cmp     [ebx+GPTH.Reserved], 0
922
        jnz     .fail_free_gpt
923
; MyLBA of GPT header at LBA X must equal X
924
        mov     eax, dword[ebx+GPTH.MyLBA+0]
925
        mov     edx, dword[ebx+GPTH.MyLBA+4]
926
        cmp     eax, dword[_mylba+0]
927
        jnz     .fail_free_gpt
928
        cmp     edx, dword[_mylba+4]
929
        jnz     .fail_free_gpt
930
; Capacity - MyLBA = AlternateLBA
931
        mov     eax, dword[esi+DISK.MediaInfo.Capacity+0]
932
        mov     edx, dword[esi+DISK.MediaInfo.Capacity+4]
933
        sub     eax, dword[_mylba+0]
934
        sbb     edx, dword[_mylba+4]
935
        cmp     eax, dword[ebx+GPTH.AlternateLBA+0]
6844 dunkaist 936
        jnz     .fail_free_gpt
6827 dunkaist 937
        cmp     edx, dword[ebx+GPTH.AlternateLBA+4]
6844 dunkaist 938
        jnz     .fail_free_gpt
6827 dunkaist 939
 
940
; Compute GPT Partition Entry Array (GPEA) length in bytes
941
        mov     eax, [ebx+GPTH.NumberOfPartitionEntries]
942
        mul     [ebx+GPTH.SizeOfPartitionEntry]
943
        test    edx, edx        ; far too big
944
        jnz     .fail_free_gpt
945
; Round up to sector boundary
946
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]    ; power of two
947
        dec     ecx
948
        add     eax, ecx
949
        jc      .fail_free_gpt  ; too big
950
        not     ecx
951
        and     eax, ecx
952
; We will need this length to compute CRC32 of GPEA
953
        mov     [GPEA_len], eax
954
; Allocate memory for GPEA
955
        stdcall kernel_alloc, eax
956
        test    eax, eax
957
        jz      .fail_free_gpt
958
; Save to not juggle with registers
959
        push    eax
960
        mov     edi, eax
961
        mov     eax, [GPEA_len]
962
        xor     edx, edx
963
; Get the number of sectors GPEA fits into
964
        div     [esi+DISK.MediaInfo.SectorSize]
965
        push    eax     ; esp = pointer to the number of sectors
966
        mov     al, DISKFUNC.read
967
        stdcall disk_call_driver, edi, dword[ebx+GPTH.PartitionEntryLBA+0], \
968
                dword[ebx+GPTH.PartitionEntryLBA+4], esp
969
        test    eax, eax
6828 dunkaist 970
        pop     eax
6827 dunkaist 971
        jnz     .fail_free_gpea_gpt
972
; Compute and check CRC32 of GPEA
973
        mov     eax, -1
974
        stdcall crc_32, 0xEDB88320, edi, [GPEA_len]
975
        xor     eax, -1
7270 dunkaist 976
        cmp     eax, [ebx+GPTH.PartitionEntryArrayCRC32]
6827 dunkaist 977
        jnz     .fail_free_gpea_gpt
978
 
979
; Process partitions, skip zeroed ones.
980
.next_gpe:
981
        xor     eax, eax
982
        mov     ecx, [ebx+GPTH.SizeOfPartitionEntry]
983
        repz scasb
984
        jz      .skip
985
        add     edi, ecx
986
        sub     edi, [ebx+GPTH.SizeOfPartitionEntry]
987
; Length of a partition in sectors is EndingLBA - StartingLBA + 1
988
        mov     eax, dword[edi+GPE.EndingLBA+0]
989
        mov     edx, dword[edi+GPE.EndingLBA+4]
990
        sub     eax, dword[edi+GPE.StartingLBA+0]
991
        sbb     edx, dword[edi+GPE.StartingLBA+4]
992
        add     eax, 1
993
        adc     edx, 0
7270 dunkaist 994
        push    ebx
995
        mov     ebx, [ebp-8] ; three-sectors-sized buffer
6827 dunkaist 996
        stdcall disk_add_partition, dword[edi+GPE.StartingLBA+0], \
997
                dword[edi+GPE.StartingLBA+4], eax, edx, esi
7270 dunkaist 998
        pop     ebx
6827 dunkaist 999
        add     edi, [ebx+GPTH.SizeOfPartitionEntry]
1000
.skip:
1001
        dec     [ebx+GPTH.NumberOfPartitionEntries]
1002
        jnz     .next_gpe
1003
 
1004
; Pointers to GPT header and GPEA are on the stack
1005
        stdcall kernel_free
1006
        stdcall kernel_free
1007
        pop     edi ebx
1008
        xor     eax, eax
1009
        ret
1010
.fail_free_gpea_gpt:
1011
        stdcall kernel_free
1012
.fail_free_gpt:
1013
        stdcall kernel_free
1014
.fail:
1015
        pop     edi ebx
1016
        xor     eax, eax
1017
        inc     eax
1018
        ret
1019
endp
1020
 
1021
; ecx = pointer to partition records array (MBR + 446)
1022
is_protective_mbr:
1023
        push    ecx edi
1024
        xor     eax, eax
6828 dunkaist 1025
        cmp     [ecx-2], ax
6827 dunkaist 1026
        jnz     .exit
1027
; Partition record 0 has specific fields
6828 dunkaist 1028
        cmp     [ecx+0], al
6827 dunkaist 1029
        jnz     .exit
1030
        cmp     byte[ecx+4], 0xEE
1031
        jnz     .exit
1032
        cmp     dword[ecx+8], 1
1033
        jnz     .exit
7546 dunkaist 1034
        mov     edi, -1
1035
        cmp     [ecx+12], edi
1036
        jz      @f
1037
        add     edi, dword[esi+DISK.MediaInfo.Capacity+0]
1038
        cmp     [ecx+12], edi
1039
        jnz     .exit
6844 dunkaist 1040
@@:
6827 dunkaist 1041
; Check that partition records 1-3 are filled with zero
1042
        lea     edi, [ecx+16]
1043
        mov     ecx, 16*3/2     ; 3 partitions
1044
        repz scasw
1045
.exit:
1046
        pop     edi ecx
1047
; Return value is ZF
1048
        ret
1049
 
2288 clevermous 1050
; This is an internal function called from disk_scan_partitions. It checks
1051
; whether the entry pointed to by ecx is a valid entry of partition table.
1052
; The entry is valid if the first byte is 0 or 80h, the first sector plus the
1053
; length is less than twice the size of media. Multiplication by two is
1054
; required since the size mentioned in the partition table can be slightly
1055
; greater than the real size.
1056
is_partition_table_entry:
1057
; 1. Check .Bootable field.
1058
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Bootable]
1059
        and     al, 7Fh
1060
        jnz     .invalid
1061
; 3. Calculate first sector + length. Note that .FirstAbsSector is relative
1062
; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length.
1063
        mov     eax, ebp
1064
        xor     edx, edx
1065
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
1066
        adc     edx, 0
1067
        add     eax, [ecx+PARTITION_TABLE_ENTRY.Length]
1068
        adc     edx, 0
1069
; 4. Divide by two.
1070
        shr     edx, 1
1071
        rcr     eax, 1
1072
; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not
1073
; overflow, this is bad.
1074
        sub     eax, dword [esi+DISK.MediaInfo.Capacity]
1075
        sbb     edx, dword [esi+DISK.MediaInfo.Capacity+4]
1076
        jnc     .invalid
1077
.valid:
1078
; 5. Return success: CF is cleared.
1079
        clc
1080
        ret
1081
.invalid:
1082
; 6. Return fail: CF is set.
1083
        stc
1084
        ret
1085
 
1086
; This is an internal function called from disk_scan_partitions. It processes
1087
; the entry pointed to by ecx.
1088
; * If the entry is invalid, just ignore this entry.
1089
; * If the type is zero, just ignore this entry.
1090
; * If the type is one of types for extended partition, store the address
1091
;   of this partition as the new MBR in [esp+4].
1092
; * Otherwise, add the partition to the list of partitions for this disk.
1093
;   We don't use the type from the entry to identify the file system;
1094
;   fs-specific checks do this more reliably.
1095
process_partition_table_entry:
1096
; 1. Check for valid entry. If invalid, return (go to 5).
1097
        call    is_partition_table_entry
1098
        jc      .nothing
1099
; 2. Check for empty entry. If invalid, return (go to 5).
1100
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Type]
1101
        test    al, al
1102
        jz      .nothing
1103
; 3. Check for extended partition. If extended, go to 6.
1104
irp type,\
1105
    0x05,\                 ; DOS: extended partition
1106
    0x0f,\                 ; WIN95: extended partition, LBA-mapped
1107
    0xc5,\                 ; DRDOS/secured: extended partition
1108
    0xd5                   ; Old Multiuser DOS secured: extended partition
1109
{
1110
        cmp     al, type
1111
        jz      .extended
1112
}
1113
; 4. If we are here, that is a normal partition. Add it to the list.
1114
; Note that the first sector is relative to MBR/EBR.
1115
        mov     eax, ebp
1116
        xor     edx, edx
1117
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
1118
        adc     edx, 0
1119
        push    ecx
1120
        stdcall disk_add_partition, eax, edx, \
3742 clevermous 1121
                [ecx+PARTITION_TABLE_ENTRY.Length], 0, esi
2288 clevermous 1122
        pop     ecx
1123
.nothing:
1124
; 5. Return.
1125
        ret
1126
.extended:
1127
; 6. If we are here, that is an extended partition. Store the address.
1128
        mov     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
1129
        mov     [esp+4], eax
1130
        ret
1131
 
1132
; This is an internal function called from disk_scan_partitions and
1133
; process_partition_table_entry. It adds one partition to the list of
1134
; partitions for the media.
3742 clevermous 1135
; Important note: start, length, disk MUST be present and
1136
; MUST be in the same order as in PARTITION structure.
1137
; esi duplicates [disk].
1138
proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword, disk:dword
2288 clevermous 1139
; 1. Check that this partition will not exceed the limit on total number.
1140
        cmp     [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS
1141
        jae     .nothing
1142
; 2. Check that this partition does not overlap with any already registered
1143
; partition. Since any file system assumes that the disk data will not change
1144
; outside of its control, such overlap could be destructive.
1145
; Since the number of partitions is usually very small and is guaranteed not
1146
; to be large, the simple linear search is sufficient.
1147
; 2a. Prepare the loop: edi will point to the current item of .Partitions
1148
; array, ecx will be the current item, ebx will hold number of items left.
1149
        mov     edi, [esi+DISK.Partitions]
1150
        mov     ebx, [esi+DISK.NumPartitions]
1151
        test    ebx, ebx
1152
        jz      .partitionok
1153
.scan_existing:
1154
; 2b. Get the next partition.
1155
        mov     ecx, [edi]
1156
        add     edi, 4
1157
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to
1158
; the left of [start, start+length) or entirely to the right.
1159
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between
1160
; cases "to the left" (2e) and "to the right" (2d).
1161
        mov     eax, dword [ecx+PARTITION.FirstSector]
1162
        mov     edx, dword [ecx+PARTITION.FirstSector+4]
1163
        sub     eax, dword [start]
1164
        sbb     edx, dword [start+4]
1165
        jb      .less
1166
; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector
1167
; is greater than or equal to start+length; the subtraction
1168
; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is
1169
; good or to 2f in the other case.
1170
        sub     eax, dword [length]
1171
        sbb     edx, dword [length+4]
1172
        jb      .overlap
1173
        jmp     .next_existing
1174
.less:
1175
; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less
1176
; than or equal to start. If the addition (.FirstSector-start) + .Length does
1177
; not cause overflow, then .FirstSector + .Length is strictly less than start;
1178
; since the equality is also valid, use decrement preliminarily. Go to 2g or
1179
; 2f depending on the overflow.
1180
        sub     eax, 1
1181
        sbb     edx, 0
1182
        add     eax, dword [ecx+PARTITION.Length]
1183
        adc     edx, dword [ecx+PARTITION.Length+4]
1184
        jnc     .next_existing
1185
.overlap:
1186
; 2f. The partition overlaps with previously registered partition. Say warning
1187
; and return with nothing done.
1188
        dbgstr 'two partitions overlap, ignoring the last one'
1189
        jmp     .nothing
1190
.next_existing:
1191
; 2g. The partition does not overlap with the current partition. Continue the
1192
; loop.
1193
        dec     ebx
1194
        jnz     .scan_existing
1195
.partitionok:
1196
; 3. The partition has passed tests. Reallocate the partitions array for a new
1197
; entry.
1198
; 3a. Call the allocator.
1199
        mov     eax, [esi+DISK.NumPartitions]
1200
        inc     eax     ; one more entry
1201
        shl     eax, 2  ; each entry is dword
1202
        call    malloc
1203
; 3b. Test the result. If failed, return with nothing done.
1204
        test    eax, eax
1205
        jz      .nothing
1206
; 3c. Copy the old array to the new array.
1207
        mov     edi, eax
1208
        push    esi
1209
        mov     ecx, [esi+DISK.NumPartitions]
1210
        mov     esi, [esi+DISK.Partitions]
1211
        rep movsd
1212
        pop     esi
1213
; 3d. Set the field in the DISK structure to the new array.
1214
        xchg    [esi+DISK.Partitions], eax
1215
; 3e. Free the old array.
1216
        call    free
1217
; 4. Recognize the file system.
1218
; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure
1219
; with possible filesystem-specific fields.
1220
        call    disk_detect_partition
1221
; 4b. Check return value. If zero, return with list not changed; so far only
1222
; the array was reallocated, this is ok for other code.
1223
        test    eax, eax
1224
        jz      .nothing
1225
; 5. Insert the new partition to the list.
1226
        stosd
1227
        inc     [esi+DISK.NumPartitions]
1228
; 6. Return.
1229
.nothing:
1230
        ret
1231
endp
1232
 
1233
; This is an internal function called from disk_add_partition.
1234
; It tries to recognize the file system on the partition and allocates the
1235
; corresponding PARTITION structure with filesystem-specific fields.
1236
disk_detect_partition:
1237
; This function inherits the stack frame from disk_add_partition. In stdcall
1238
; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp
1239
; and [ebp+4]=return address.
1240
virtual at ebp+8
1241
.start  dq      ?
1242
.length dq      ?
3742 clevermous 1243
.disk   dd      ?
2288 clevermous 1244
end virtual
3742 clevermous 1245
; 1. Read the bootsector to the buffer.
2643 clevermous 1246
; When disk_add_partition is called, ebx contains a pointer to
3742 clevermous 1247
; a three-sectors-sized buffer. This function saves ebx in the stack
2643 clevermous 1248
; immediately before ebp.
3742 clevermous 1249
        mov     ebx, [ebp-4] ; get buffer
5089 clevermous 1250
        add     ebx, [esi+DISK.MediaInfo.SectorSize] ; advance over MBR data to bootsector data
3742 clevermous 1251
        add     ebp, 8       ; ebp points to part of PARTITION structure
1252
        xor     eax, eax     ; first sector of the partition
1253
        call    fs_read32_sys
2643 clevermous 1254
; 2. Run tests for all supported filesystems. If at least one test succeeded,
1255
; go to 4.
3742 clevermous 1256
; For tests:
1257
; ebp -> first three fields of PARTITION structure, .start, .length, .disk;
1258
; [esp] = error code after bootsector read: 0 = ok, otherwise = failed,
1259
; ebx points to the buffer for bootsector,
5089 clevermous 1260
; ebx+[esi+DISK.MediaInfo.SectorSize] points to sector-sized buffer that can be used for anything.
9930 Doczom 1261
 
1262
        ; lock fs list
1263
 
9894 Doczom 1264
        mov     ecx, [fs_list]
1265
@@:
1266
        cmp     ecx, fs_list
1267
        jz      @f
1268
 
1269
        push    ecx eax
1270
        call    dword[ecx + FileSystem.Creat_part]
1271
        pop     ecx
1272
        test    eax, eax
1273
        jnz     .success
1274
 
9930 Doczom 1275
        mov     eax, ecx
1276
        pop     ecx
9894 Doczom 1277
        mov     ecx, [ecx]
1278
        jmp     @b
1279
@@:
1280
        push    eax
2643 clevermous 1281
        call    fat_create_partition
1282
        test    eax, eax
1283
        jnz     .success
9734 sober_dev 1284
        call    exFAT_create_partition
1285
        test    eax, eax
1286
        jnz     .success
3742 clevermous 1287
        call    ntfs_create_partition
1288
        test    eax, eax
1289
        jnz     .success
1290
        call    ext2_create_partition
1291
        test    eax, eax
1292
        jnz     .success
3913 dunkaist 1293
        call    xfs_create_partition
1294
        test    eax, eax
1295
        jnz     .success
10053 Doczom 1296
        call    iso9660_create_partition
1297
        test    eax, eax
1298
        jnz     .success
2643 clevermous 1299
; 3. No file system has recognized the volume, so just allocate the PARTITION
2288 clevermous 1300
; structure without extra fields.
3598 clevermous 1301
        movi    eax, sizeof.PARTITION
2288 clevermous 1302
        call    malloc
1303
        test    eax, eax
1304
        jz      .nothing
3742 clevermous 1305
        mov     edx, dword [ebp+PARTITION.FirstSector]
2288 clevermous 1306
        mov     dword [eax+PARTITION.FirstSector], edx
3742 clevermous 1307
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
2288 clevermous 1308
        mov     dword [eax+PARTITION.FirstSector+4], edx
3742 clevermous 1309
        mov     edx, dword [ebp+PARTITION.Length]
2288 clevermous 1310
        mov     dword [eax+PARTITION.Length], edx
3742 clevermous 1311
        mov     edx, dword [ebp+PARTITION.Length+4]
2288 clevermous 1312
        mov     dword [eax+PARTITION.Length+4], edx
2643 clevermous 1313
        mov     [eax+PARTITION.Disk], esi
3742 clevermous 1314
        mov     [eax+PARTITION.FSUserFunctions], default_fs_functions
2643 clevermous 1315
.success:
2288 clevermous 1316
.nothing:
3742 clevermous 1317
        sub     ebp, 8 ; restore ebp
2643 clevermous 1318
; 4. Return with eax = pointer to PARTITION or NULL.
1319
        pop     ecx
9930 Doczom 1320
 
1321
        ; unlock fs list
2288 clevermous 1322
        ret
1323
 
3742 clevermous 1324
iglobal
1325
align 4
1326
default_fs_functions:
1327
        dd      free
9043 dunkaist 1328
        dd      (default_fs_functions_end - default_fs_functions - 4) / 4
1329
        dd      0
1330
        dd      0
1331
        dd      0
1332
        dd      0
1333
        dd      0
1334
        dd      default_fs_get_file_info
1335
default_fs_functions_end:
3742 clevermous 1336
endg
1337
 
9043 dunkaist 1338
proc default_fs_get_file_info uses edi
1339
        movi    eax, ERROR_UNSUPPORTED_FS
1340
        cmp     byte[esi], 0
1341
        jnz     .done
1342
        movi    ecx, 40 ; len of BDFE without filename
1343
        cmp     [ebx+f70s5arg.xflags], 0
1344
        jz      @f
1345
        add     ecx, 2  ; volume label requested, space for utf16 terminator
1346
@@:
1347
        mov     ebx, [ebx+f70s5arg.buf]
1348
        stdcall is_region_userspace, ebx, ecx
1349
        movi    eax, ERROR_MEMORY_POINTER
9045 dunkaist 1350
        jnz     .done
9043 dunkaist 1351
        mov     edi, ebx
1352
        xor     eax, eax
1353
        rep stosb
1354
        mov     [ebx+bdfe.attr], 0x10   ; directory flag
1355
        mov     word[ebx+bdfe.name], 0  ; word because of possible utf16
1356
        mov     eax, dword[ebp+PARTITION.Length+DQ.lo]
1357
        mov     edx, dword[ebp+PARTITION.Length+DQ.hi]
1358
        mov     ecx, [ebp+PARTITION.Disk]
1359
        mov     ecx, [ecx+DISK.MediaInfo.SectorSize]
1360
        bsf     ecx, ecx
1361
        shld    edx, eax, cl
1362
        shl     eax, cl
1363
        mov     [ebx+bdfe.size.lo], eax
1364
        mov     [ebx+bdfe.size.hi], edx
1365
        xor     eax, eax
1366
.done:
1367
        ret
1368
endp
1369
 
2288 clevermous 1370
; This function is called from file_system_lfn.
1371
; This handler gets the control each time when fn 70 is called
1372
; with unknown item of root subdirectory.
6468 pathoswith 1373
; in: esi = ebp -> path string
2288 clevermous 1374
; out: if the handler processes path, it must not return in file_system_lfn,
1375
;      but instead pop return address and return directly to the caller
1376
;      otherwise simply return
1377
dyndisk_handler:
9734 sober_dev 1378
;       DEBUGF  1, "K : FS Input Path:%s\n",esi
2288 clevermous 1379
        push    ebx edi         ; save registers used in file_system_lfn
1380
; 1. Acquire the mutex.
1381
        mov     ecx, disk_list_mutex
1382
        call    mutex_lock
1383
; 2. Loop over the list of DISK structures.
1384
; 2a. Initialize.
1385
        mov     ebx, disk_list
1386
.scan:
1387
; 2b. Get the next item.
1388
        mov     ebx, [ebx+DISK.Next]
1389
; 2c. Check whether the list is done. If so, go to 3.
1390
        cmp     ebx, disk_list
1391
        jz      .notfound
1392
; 2d. Compare names. If names match, go to 5.
1393
        mov     edi, [ebx+DISK.Name]
1394
        push    esi
1395
@@:
1396
; esi points to the name from fs operation; it is terminated by zero or slash.
1397
        lodsb
1398
        test    al, al
1399
        jz      .eoin_dec
1400
        cmp     al, '/'
1401
        jz      .eoin
1402
; edi points to the disk name.
1403
        inc     edi
1404
; edi points to lowercase name, this is a requirement for the driver.
1405
; Characters at esi can have any register. Lowercase the current character.
1406
; This lowercasing works for latin letters and digits; since the disk name
1407
; should not contain other symbols, this is ok.
1408
        or      al, 20h
1409
        cmp     al, [edi-1]
1410
        jz      @b
1411
.wrongname:
1412
; 2f. Names don't match. Continue the loop.
1413
        pop     esi
1414
        jmp     .scan
1415
.notfound:
1416
; The loop is done and no name matches.
1417
; 3. Release the mutex.
1418
        call    mutex_unlock
1419
; 4. Return normally.
1420
        pop     edi ebx         ; restore registers used in file_system_lfn
1421
        ret
1422
; part of 2d: the name matches partially, but we must check that this is full
1423
; equality.
1424
.eoin_dec:
1425
        dec     esi
1426
.eoin:
1427
        cmp     byte [edi], 0
1428
        jnz     .wrongname
1429
; We found the addressed DISK structure.
1430
; 5. Reference the disk.
1431
        lock inc [ebx+DISK.RefCount]
1432
; 6. Now we are sure that the DISK structure is not going to die at least
1433
; while we are working with it, so release the global mutex.
1434
        call    mutex_unlock
2643 clevermous 1435
        pop     ecx             ; pop from the stack saved value of esi
2288 clevermous 1436
; 7. Acquire the mutex for media object.
1437
        pop     edi             ; restore edi
1438
        lea     ecx, [ebx+DISK.MediaLock]
1439
        call    mutex_lock
1440
; 8. Get the media object. If it is not NULL, reference it.
1441
        xor     edx, edx
1442
        cmp     [ebx+DISK.MediaInserted], dl
1443
        jz      @f
1444
        mov     edx, ebx
1445
        inc     [ebx+DISK.MediaRefCount]
1446
@@:
1447
; 9. Now we are sure that the media object, if it exists, is not going to die
1448
; at least while we are working with it, so release the mutex for media object.
1449
        call    mutex_unlock
1450
        mov     ecx, ebx
1451
        pop     ebx eax         ; restore ebx, pop return address
1452
; 10. Check whether the fs operation wants to enumerate partitions (go to 11)
1453
; or work with some concrete partition (go to 12).
1454
        cmp     byte [esi], 0
1455
        jnz     .haspartition
1456
; 11. The fs operation wants to enumerate partitions.
6875 pathoswith 1457
; Check whether the media is inserted.
2288 clevermous 1458
        mov     esi, fs_dyndisk_next_nomedia
1459
        test    edx, edx
1460
        jz      @f
1461
        mov     esi, fs_dyndisk_next
6875 pathoswith 1462
@@: ; Let the procedure from fs_lfn.inc do the job.
2288 clevermous 1463
        jmp     file_system_lfn.maindir_noesi
6468 pathoswith 1464
 
6845 pathoswith 1465
.root:
1466
        pop     ecx edx
1467
        xor     eax, eax
1468
        cmp     byte [ebx], 9
1469
        jz      .cleanup_ecx
6468 pathoswith 1470
.access_denied:
6845 pathoswith 1471
        movi    eax, ERROR_ACCESS_DENIED
1472
.cleanup_ecx:
1473
        mov     [esp+32], eax
6468 pathoswith 1474
        mov     esi, ecx        ; disk*dereference assume that esi points to DISK
1475
        test    edx, edx        ; if there are no media, we didn't reference it
1476
        jz      @f
1477
        call    disk_media_dereference
1478
@@:
1479
        call    disk_dereference
1480
        stdcall kernel_free, ebp
1481
        ret
1482
 
1483
.dyndisk_cleanup:
6845 pathoswith 1484
        pop     ecx edx
1485
        movi    eax, ERROR_FILE_NOT_FOUND
1486
        jmp     .cleanup_ecx
6468 pathoswith 1487
 
2288 clevermous 1488
.haspartition:
1489
; 12. The fs operation has specified some partition.
6845 pathoswith 1490
        push    edx ecx
6464 pathoswith 1491
        xor     eax, eax
1492
        lodsb
1493
        sub     eax, '0'
1494
        jz      .dyndisk_cleanup
1495
        cmp     eax, 10
1496
        jnc     .dyndisk_cleanup
1497
        mov     ecx, eax
1498
        lodsb
1499
        cmp     eax, '/'
1500
        jz      @f
1501
        test    eax, eax
1502
        jnz     .dyndisk_cleanup
1503
        dec     esi
1504
@@:
6845 pathoswith 1505
        cmp     byte [esi], 0
1506
        jnz     @f
10053 Doczom 1507
        ;  partition  info
6845 pathoswith 1508
        cmp     byte [ebx], 1
1509
        jz      @f
1510
        cmp     byte [ebx], 5
1511
        jnz     .root
1512
@@:
2288 clevermous 1513
        dec     ecx     ; convert to zero-based partition index
6464 pathoswith 1514
        pop     edx     ; edx = pointer to DISK, dword [esp] = NULL or edx
4273 clevermous 1515
; If the driver does not support insert notifications and we are the only fs
1516
; operation with this disk, ask the driver whether the media
1517
; was inserted/removed/changed. Otherwise, assume that media status is valid.
1518
        test    byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
1519
        jz      .media_accurate
10053 Doczom 1520
 
4273 clevermous 1521
        push    ecx esi
1522
        mov     esi, edx
1523
        cmp     dword [esp+8], 0
1524
        jz      .test_no_media
1525
        cmp     [esi+DISK.MediaRefCount], 2
1526
        jnz     .media_accurate_pop
10053 Doczom 1527
 
4273 clevermous 1528
        lea     edx, [esi+DISK.MediaInfo]
1529
        and     [edx+DISKMEDIAINFO.Flags], 0
1530
        mov     al, DISKFUNC.querymedia
1531
        stdcall disk_call_driver, edx
2288 clevermous 1532
        test    eax, eax
4273 clevermous 1533
        jz      .media_accurate_pop
10053 Doczom 1534
 
4273 clevermous 1535
        stdcall disk_media_dereference  ; drop our reference so that disk_media_changed could close the media
1536
        stdcall disk_media_changed, esi, 0
1537
        and     dword [esp+8], 0        ; no media
10053 Doczom 1538
        ;jmp     .media_accurate_pop
4273 clevermous 1539
.test_no_media:
1540
        stdcall disk_media_changed, esi, 1      ; issue fake notification
6468 pathoswith 1541
; if querymedia() inside disk_media_changed returns error, the notification is ignored
4273 clevermous 1542
        cmp     [esi+DISK.MediaInserted], 0
1543
        jz      .media_accurate_pop
1544
        lock inc [esi+DISK.MediaRefCount]
1545
        mov     dword [esp+8], esi
10053 Doczom 1546
 
4273 clevermous 1547
.media_accurate_pop:
1548
        mov     edx, esi
1549
        pop     esi ecx
10053 Doczom 1550
 
4273 clevermous 1551
.media_accurate:
1552
        pop     eax
1553
        test    eax, eax
2288 clevermous 1554
        jz      .nomedia
1555
        cmp     ecx, [edx+DISK.NumPartitions]
6468 pathoswith 1556
        jae     .notfound2
2643 clevermous 1557
        mov     eax, [edx+DISK.Partitions]
1558
        mov     eax, [eax+ecx*4]
1559
        mov     edi, [eax+PARTITION.FSUserFunctions]
1560
        mov     ecx, [ebx]
3742 clevermous 1561
        cmp     [edi+4], ecx
2643 clevermous 1562
        jbe     .unsupported
9043 dunkaist 1563
        cmp     dword[edi+8+ecx*4], 0   ; user function not implemented
1564
        jz      .unsupported
6917 pathoswith 1565
        pushd   edx ebp eax [edi+8+ecx*4]
1566
        cmp     ecx, 10
1567
        jnz     .callFS
1568
        or      ecx, -1
1569
        mov     edi, esi
1570
        xor     eax, eax
1571
        repnz scasb
1572
        mov     edx, edi
1573
        dec     edi
1574
        mov     al, '/'
1575
        std
1576
        repnz scasb
1577
        cld
1578
        inc     edi
1579
        mov     [edi], ah
1580
        mov     ebp, [current_slot]
1581
        add     ebp, APPDATA.cur_dir
1582
        pushd   ebx esi edx [ebp] ebp edi
1583
        sub     esi, 2
1584
        mov     [ebp], esi
1585
        mov     edi, edx
1586
        mov     esi, [ebx+16]
1587
        mov     eax, [ebx+20]
1588
        cmp     eax, 4
1589
        jc      @f
1590
        xor     eax, eax
1591
@@:
1592
        call    getFullPath
1593
        pop     edi ebp
1594
        mov     byte [edi], '/'
1595
        popd    [ebp] edi esi ebx
7040 pathoswith 1596
        add     edi, 2
6917 pathoswith 1597
        test    eax, eax
1598
        jz      .errorRename
7040 pathoswith 1599
        cmp     byte [edi], 0
1600
        jz      .errorRename
6917 pathoswith 1601
.callFS:
1602
        pop     eax ebp
1603
        call    eax
1604
        pop     ebp edx
2643 clevermous 1605
        mov     dword [esp+20], ebx
2288 clevermous 1606
.cleanup:
6845 pathoswith 1607
        mov     dword [esp+32], eax
2288 clevermous 1608
        mov     esi, edx
1609
        call    disk_media_dereference
6468 pathoswith 1610
@@:
2288 clevermous 1611
        call    disk_dereference
6468 pathoswith 1612
        stdcall kernel_free, ebp
2288 clevermous 1613
        ret
6468 pathoswith 1614
 
1615
.unsupported:
6845 pathoswith 1616
        movi    eax, ERROR_UNKNOWN_FS
6468 pathoswith 1617
        cmp     edi, default_fs_functions
1618
        jz      .cleanup
6845 pathoswith 1619
        movi    eax, ERROR_UNSUPPORTED_FS
2643 clevermous 1620
        jmp     .cleanup
6468 pathoswith 1621
 
6917 pathoswith 1622
.errorRename:
1623
        pop     eax eax ebp edx
6468 pathoswith 1624
.notfound2:
6845 pathoswith 1625
        movi    eax, ERROR_FILE_NOT_FOUND
2288 clevermous 1626
        jmp     .cleanup
6468 pathoswith 1627
 
2288 clevermous 1628
.nomedia:
1629
        test    ecx, ecx
6468 pathoswith 1630
        jnz     .notfound2
2288 clevermous 1631
        mov     dword [esp+32], ERROR_DEVICE
1632
        mov     esi, edx
6468 pathoswith 1633
        jmp     @b
1634
 
1635
; This is a callback for enumerating partitions called from
1636
; file_system_lfn.maindir in the case of inserted media.
1637
; It just increments eax until DISK.NumPartitions reached and then
1638
; cleans up.
1639
fs_dyndisk_next:
1640
        mov     ecx, [esp+8]
1641
        cmp     eax, [ecx+DISK.NumPartitions]
1642
        jae     .nomore
1643
        inc     eax
1644
        clc
1645
        ret
1646
.nomore:
1647
        pusha
1648
        mov     esi, ecx
1649
        call    disk_media_dereference
2288 clevermous 1650
        call    disk_dereference
6468 pathoswith 1651
        popa
1652
        stc
2288 clevermous 1653
        ret
1654
 
6468 pathoswith 1655
; This is a callback for enumerating partitions called from
1656
; file_system_lfn.maindir in the case of missing media.
1657
; In this case we create one pseudo-partition.
1658
fs_dyndisk_next_nomedia:
1659
        cmp     eax, 1
1660
        jae     .nomore
1661
        inc     eax
1662
        clc
1663
        ret
1664
.nomore:
1665
        mov     ecx, [esp+8]
1666
        pusha
1667
        mov     esi, ecx
1668
        call    disk_dereference
1669
        popa
1670
        stc
1671
        ret
1672
 
2288 clevermous 1673
; This function is called from file_system_lfn.
1674
; This handler is called when virtual root is enumerated
1675
; and must return all items which can be handled by this.
1676
; It is called several times, first time with eax=0
1677
; in: eax = 0 for first call, previously returned value for subsequent calls
1678
; out: eax = 0 => no more items
1679
;      eax != 0 => buffer pointed to by edi contains name of item
1680
dyndisk_enum_root:
1681
        push    edx             ; save register used in file_system_lfn
1682
        mov     ecx, disk_list_mutex    ; it will be useful
1683
; 1. If this is the first call, acquire the mutex and initialize.
1684
        test    eax, eax
1685
        jnz     .notfirst
1686
        call    mutex_lock
1687
        mov     eax, disk_list
1688
.notfirst:
1689
; 2. Get next item.
1690
        mov     eax, [eax+DISK.Next]
1691
; 3. If there are no more items, go to 6.
1692
        cmp     eax, disk_list
1693
        jz      .last
1694
; 4. Copy name from the DISK structure to edi.
1695
        push    eax esi
1696
        mov     esi, [eax+DISK.Name]
1697
@@:
1698
        lodsb
1699
        stosb
1700
        test    al, al
1701
        jnz     @b
1702
        pop     esi eax
1703
; 5. Return with eax = item.
1704
        pop     edx             ; restore register used in file_system_lfn
1705
        ret
1706
.last:
1707
; 6. Release the mutex and return with eax = 0.
1708
        call    mutex_unlock
1709
        xor     eax, eax
1710
        pop     edx             ; restore register used in file_system_lfn
1711
        ret
10053 Doczom 1712
 
1713
; void fastcall eject_disk_media(const char* str, uint32_t code);
1714
; for load/eject drive
1715
; ecx -> asciiz string disk name (sd0, usbhd0)
1716
; edx = code command(0 - LoadTray 1-EjectTray)
1717
eject_disk_media:
1718
        push    esi edx ecx
1719
 
1720
        mov     ecx, disk_list_mutex
1721
        call    mutex_lock
1722
        ; find disk ejected
1723
        mov     edx, disk_list
1724
.next:
1725
        mov     edx, [edx]
1726
        cmp     edx, disk_list
1727
        jz      .nf
1728
 
1729
        mov     ecx, [edx + DISK.Name]
1730
        mov     esi, [esp]
1731
@@:
1732
        lodsb
1733
        test    al, al
1734
        jz      @f
1735
 
1736
        or      al, 0x20
1737
        cmp     al, [ecx]
1738
        jnz     .next
1739
        inc     ecx
1740
        jmp     @b
1741
@@:
1742
        cmp     byte[ecx], al
1743
        jnz     .next
1744
 
1745
        mov     ecx, disk_list_mutex
1746
        call    mutex_unlock
1747
; found disk
1748
        pop     ecx edx esi
1749
 
1750
        and     edx, 1b
1751
        mov     al, DISKFUNC.LoadTray
1752
        stdcall disk_call_driver, edx   ; ecx - code command
1753
        ret
1754
 
1755
.nf:
1756
        mov     ecx, disk_list_mutex
1757
        call    mutex_unlock
1758
        pop     ecx edx esi
1759
        ret
1760
 
1761
; for disk emulation PARTITION structure
1762
; int32_t find_part(char* esi) esi -> name disk and partition
1763
; return number partition or zero for disk and
1764
;find_part:
1765
;        mov     ecx, disk_list_mutex
1766
;
1767
;        ret