Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
2142 serge 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
5565 serge 3
;; Copyright (C) KolibriOS team 2011-2015. All rights reserved. ;;
2142 serge 4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
3908 Serge 8
$Revision: 3742 $
2142 serge 9
 
10
; =============================================================================
11
; ================================= Constants =================================
12
; =============================================================================
13
; Error codes for callback functions.
3555 Serge 14
DISK_STATUS_OK              = 0 ; success
2142 serge 15
DISK_STATUS_GENERAL_ERROR   = -1; if no other code is suitable
16
DISK_STATUS_INVALID_CALL    = 1 ; invalid input parameters
3555 Serge 17
DISK_STATUS_NO_MEDIA        = 2 ; no media present
2142 serge 18
DISK_STATUS_END_OF_MEDIA    = 3 ; end of media while reading/writing data
5201 serge 19
DISK_STATUS_NO_MEMORY       = 4 ; insufficient memory for driver operation
2142 serge 20
; Driver flags. Represent bits in DISK.DriverFlags.
21
DISK_NO_INSERT_NOTIFICATION = 1
22
; Media flags. Represent bits in DISKMEDIAINFO.Flags.
23
DISK_MEDIA_READONLY = 1
24
 
2268 Serge 25
; If too many partitions are detected,there is probably an error on the disk.
2142 serge 26
; 256 partitions should be enough for any reasonable use.
2268 Serge 27
; Also, the same number is limiting the number of MBRs to process; if
28
; too many MBRs are visible,there probably is a loop in the MBR structure.
2142 serge 29
MAX_NUM_PARTITIONS = 256
30
 
31
; =============================================================================
32
; ================================ Structures =================================
33
; =============================================================================
34
; This structure defines all callback functions for working with the physical
35
; device. They are implemented by a driver. Objects with this structure reside
36
; in a driver.
3555 Serge 37
struct  DISKFUNC
2434 Serge 38
        strucsize       dd ?
2142 serge 39
; Size of the structure. This field is intended for possible extensions of
40
; this structure. If a new function is added to this structure and a driver
41
; implements an old version, the caller can detect this by checking .strucsize,
42
; so the driver remains compatible.
2434 Serge 43
        close           dd ?
2142 serge 44
; The pointer to the function which frees all driver-specific resources for
45
; the disk.
46
; Optional, may be NULL.
47
; void close(void* userdata);
2434 Serge 48
        closemedia      dd ?
2142 serge 49
; The pointer to the function which informs the driver that the kernel has
50
; finished all processing with the current media. If media is removed, the
51
; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA,
52
; even if new media is inserted, until this function is called. If media is
53
; removed, a new call to 'disk_media_changed' is not allowed until this
54
; function is called.
55
; Optional, may be NULL (if media is not removable).
56
; void closemedia(void* userdata);
2434 Serge 57
        querymedia      dd ?
2142 serge 58
; The pointer to the function which determines capabilities of the media.
59
; int querymedia(void* userdata, DISKMEDIAINFO* info);
60
; Return value: one of DISK_STATUS_*
2434 Serge 61
        read            dd ?
2142 serge 62
; The pointer to the function which reads data from the device.
63
; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors);
64
; input: *numsectors = number of sectors to read
65
; output: *numsectors = number of sectors which were successfully read
66
; Return value: one of DISK_STATUS_*
2434 Serge 67
        write           dd ?
2142 serge 68
; The pointer to the function which writes data to the device.
69
; Optional, may be NULL.
70
; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors);
71
; input: *numsectors = number of sectors to write
72
; output: *numsectors = number of sectors which were successfully written
73
; Return value: one of DISK_STATUS_*
2434 Serge 74
        flush           dd ?
2142 serge 75
; The pointer to the function which flushes the internal device cache.
76
; Optional, may be NULL.
77
; int flush(void* userdata);
78
; Return value: one of DISK_STATUS_*
79
; Note that read/write are called by the cache manager, so a driver should not
80
; create a software cache. This function is implemented for flushing a hardware
81
; cache, if it exists.
2434 Serge 82
        adjust_cache_size       dd ?
2142 serge 83
; The pointer to the function which returns the cache size for this device.
84
; Optional, may be NULL.
85
; unsigned int adjust_cache_size(unsigned int suggested_size);
86
; Return value: 0 = disable cache, otherwise = used cache size in bytes.
87
ends
88
 
2268 Serge 89
; This structure holds information on a medium.
90
; Objects with this structure are allocated by the kernel as a part of the DISK
91
; structure and are filled by a driver in the 'querymedia' callback.
3555 Serge 92
struct  DISKMEDIAINFO
2434 Serge 93
        Flags           dd ?
2142 serge 94
; Combination of DISK_MEDIA_* bits.
2434 Serge 95
        SectorSize      dd ?
2142 serge 96
; Size of the sector.
2434 Serge 97
        Capacity        dq ?
2142 serge 98
; Size of the media in sectors.
99
ends
100
 
2268 Serge 101
; This structure represents the disk cache. To follow the old implementation,
102
; there are two distinct caches for a disk, one for "system" data,and the other
2142 serge 103
; for "application" data.
3555 Serge 104
struct  DISKCACHE
2142 serge 105
; The following fields are inherited from data32.inc:cache_ideX.
2434 Serge 106
        pointer         dd ?
107
        data_size       dd ?    ; unused
108
        data            dd ?
109
        sad_size        dd ?
110
        search_start    dd ?
5201 serge 111
        sector_size_log dd ?
2142 serge 112
ends
113
 
114
; This structure represents a disk device and its media for the kernel.
115
; This structure is allocated by the kernel in the 'disk_add' function,
116
; freed in the 'disk_dereference' function.
3555 Serge 117
struct  DISK
2142 serge 118
; Fields of disk object
2434 Serge 119
        Next            dd ?
120
        Prev            dd ?
2142 serge 121
; All disk devices are linked in one list with these two fields.
122
; Head of the list is the 'disk_list' variable.
2434 Serge 123
        Functions       dd ?
2142 serge 124
; Pointer to the 'DISKFUNC' structure with driver functions.
2434 Serge 125
        Name            dd ?
2142 serge 126
; Pointer to the string used for accesses through the global filesystem.
2434 Serge 127
        UserData        dd ?
2142 serge 128
; This field is passed to all callback functions so a driver can decide which
129
; physical device is addressed.
2434 Serge 130
        DriverFlags     dd ?
2142 serge 131
; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined.
132
; If it is set, the driver will never issue 'disk_media_changed' notification
133
; with argument set to true, so the kernel must try to detect media during
134
; requests from the file system.
2434 Serge 135
        RefCount        dd ?
2142 serge 136
; Count of active references to this structure. One reference is kept during
137
; the lifetime of the structure between 'disk_add' and 'disk_del'.
138
; Another reference is taken during any filesystem operation for this disk.
139
; One reference is added if media is inserted.
140
; The structure is destroyed when the reference count decrements to zero:
141
; this usually occurs in 'disk_del', but can be delayed to the end of last
142
; filesystem operation, if one is active.
2434 Serge 143
        MediaLock       MUTEX
2142 serge 144
; Lock to protect the MEDIA structure. See the description after
145
; 'disk_list_mutex' for the locking strategy.
146
; Fields of media object
2434 Serge 147
        MediaInserted   db ?
2142 serge 148
; 0 if media is not inserted, nonzero otherwise.
2434 Serge 149
        MediaUsed       db ?
2142 serge 150
; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is
151
; nonzero, this field is nonzero too; however, when .MediaRefCount goes
152
; to zero, there is some time interval during which media object is still used.
3500 Serge 153
                        dw ? ; padding
2142 serge 154
; The following fields are not valid unless either .MediaInserted is nonzero
155
; or they are accessed from a code which has obtained the reference when
156
; .MediaInserted was nonzero.
2434 Serge 157
        MediaRefCount   dd ?
2142 serge 158
; Count of active references to the media object. One reference is kept during
159
; the lifetime of the media between two calls to 'disk_media_changed'.
160
; Another reference is taken during any filesystem operation for this media.
161
; The callback 'closemedia' is called when the reference count decrements to
162
; zero: this usually occurs in 'disk_media_changed', but can be delayed to the
2268 Serge 163
; end of the last filesystem operation, if one is active.
2434 Serge 164
        MediaInfo       DISKMEDIAINFO
2268 Serge 165
; This field keeps information on the current media.
2434 Serge 166
        NumPartitions   dd ?
2142 serge 167
; Number of partitions on this media.
2434 Serge 168
        Partitions      dd ?
2142 serge 169
; Pointer to array of .NumPartitions pointers to PARTITION structures.
2434 Serge 170
        cache_size      dd ?
2142 serge 171
; inherited from cache_ideX_size
5201 serge 172
        CacheLock       MUTEX
173
; Lock to protect both caches.
2434 Serge 174
        SysCache        DISKCACHE
175
        AppCache        DISKCACHE
2142 serge 176
; Two caches for the disk.
177
ends
178
 
179
; This structure represents one partition for the kernel. This is a base
180
; template, the actual contents after common fields is determined by the
181
; file system code for this partition.
3555 Serge 182
struct  PARTITION
2434 Serge 183
        FirstSector     dq ?
2142 serge 184
; First sector of the partition.
2434 Serge 185
        Length          dq ?
2142 serge 186
; Length of the partition in sectors.
2434 Serge 187
        Disk            dd ?
2142 serge 188
; Pointer to parent DISK structure.
2434 Serge 189
        FSUserFunctions dd ?
2142 serge 190
; Handlers for the sysfunction 70h. This field is a pointer to the following
3908 Serge 191
; array. The first dword is pointer to disconnect handler.
192
; The first dword is a number of supported subfunctions, other dwords
2142 serge 193
; point to handlers of corresponding subfunctions.
194
; ...fs-specific data may follow...
195
ends
196
 
197
; This is an external structure, it represents an entry in the partition table.
3555 Serge 198
struct  PARTITION_TABLE_ENTRY
2434 Serge 199
        Bootable        db ?
2142 serge 200
; 80h = bootable partition, 0 = non-bootable partition, other values = invalid
2434 Serge 201
        FirstHead       db ?
202
        FirstSector     db ?
203
        FirstTrack      db ?
2142 serge 204
; Coordinates of first sector in CHS.
2434 Serge 205
        Type            db ?
2142 serge 206
; Partition type, one of predefined constants. 0 = empty, several types denote
207
; extended partition (see process_partition_table_entry), we are not interested
208
; in other values.
2434 Serge 209
        LastHead        db ?
210
        LastSector      db ?
211
        LastTrack       db ?
2142 serge 212
; Coordinates of last sector in CHS.
2434 Serge 213
        FirstAbsSector  dd ?
2142 serge 214
; Coordinate of first sector in LBA.
2434 Serge 215
        Length          dd ?
2142 serge 216
; Length of the partition in sectors.
217
ends
218
 
219
; =============================================================================
220
; ================================ Global data ================================
221
; =============================================================================
222
iglobal
223
; The pseudo-item for the list of all DISK structures.
224
; Initialized to the empty list.
225
disk_list:
3555 Serge 226
        dd      disk_list
227
        dd      disk_list
2142 serge 228
endg
229
uglobal
230
; This mutex guards all operations with the global list of DISK structures.
231
disk_list_mutex MUTEX
2268 Serge 232
; * There are two dependent objects, a disk and a media. In the simplest case,
2142 serge 233
;   disk and media are both non-removable. However, in the general case both
2268 Serge 234
;   can be removed at any time, simultaneously or only media,and this makes things
2142 serge 235
;   complicated.
236
; * For efficiency, both disk and media objects are located in the one
237
;   structure named DISK. However, logically they are different.
238
; * The following operations use data of disk object: adding (disk_add);
239
;   deleting (disk_del); filesystem (fs_lfn which eventually calls
240
;   dyndisk_handler or dyndisk_enum_root).
241
; * The following operations use data of media object: adding/removing
242
;   (disk_media_changed); filesystem (fs_lfn which eventually calls
243
;   dyndisk_handler; dyndisk_enum_root doesn't work with media).
244
; * Notifications disk_add, disk_media_changed, disk_del are synchronized
245
;   between themselves, this is a requirement for the driver. However, file
246
;   system operations are asynchronous, can be issued at any time by any
247
;   thread.
248
; * We must prevent a situation when a filesystem operation thinks that the
249
;   object is still valid but in fact the notification has destroyed the
250
;   object. So we keep a reference counter for both disk and media and destroy
251
;   the object when this counter goes to zero.
252
; * The driver must know when it is safe to free driver-allocated resources.
253
;   The object can be alive even after death notification has completed.
254
;   We use special callbacks to satisfy both assertions: 'close' for the disk
255
;   and 'closemedia' for the media. The destruction of the object includes
256
;   calling the corresponding callback.
257
; * Each filesystem operation keeps one reference for the disk and one
258
;   reference for the media. Notification disk_del forces notification on the
259
;   media death, so the reference counter for the disk is always not less than
260
;   the reference counter for the media.
261
; * Two operations "get the object" and "increment the reference counter" can
262
;   not be done simultaneously. We use a mutex to guard the consistency here.
263
;   It must be a part of the container for the object, so that this mutex can
264
;   be acquired as a part of getting the object from the container. The
265
;   container for disk object is the global list, and this list is guarded by
266
;   'disk_list_mutex'. The container for media object is the disk object, and
267
;   the corresponding mutex is DISK.MediaLock.
268
; * Notifications do not change the data of objects, they can only remove
269
;   objects. Thus we don't need another synchronization at this level. If two
270
;   filesystem operations are referencing the same filesystem data, this is
271
;   better resolved at the level of the filesystem.
272
endg
273
 
274
iglobal
5201 serge 275
; The function 'disk_scan_partitions' needs three sector-sized buffers for
2142 serge 276
; MBR, bootsector and fs-temporary sector data. It can not use the static
277
; buffers always, since it can be called for two or more disks in parallel.
278
; However, this case is not typical. We reserve three static 512-byte buffers
279
; and a flag that these buffers are currently used. If 'disk_scan_partitions'
280
; detects that the buffers are currently used, it allocates buffers from the
5201 serge 281
; heap. Also, the heap is used when sector size is other than 512.
2142 serge 282
; The flag is implemented as a global dword variable. When the static buffers
283
; are not used, the value is -1. When the static buffers are used, the value
284
; is normally 0 and temporarily can become greater. The function increments
285
; this value. If the resulting value is zero, it uses the buffers and
286
; decrements the value when the job is done. Otherwise, it immediately
287
; decrements the value and uses buffers from the heap, allocated in the
288
; beginning and freed in the end.
3555 Serge 289
partition_buffer_users  dd      -1
2142 serge 290
endg
291
uglobal
292
; The static buffers for MBR, bootsector and fs-temporary sector data.
293
align 16
3555 Serge 294
mbr_buffer      rb      512
295
bootsect_buffer rb      512
296
fs_tmp_buffer   rb      512
2142 serge 297
endg
298
 
299
iglobal
300
; This is the array of default implementations of driver callbacks.
301
; Same as DRIVERFUNC structure except for the first field; all functions must
302
; have the default implementations.
303
align 4
304
disk_default_callbacks:
3555 Serge 305
        dd      disk_default_close
306
        dd      disk_default_closemedia
307
        dd      disk_default_querymedia
308
        dd      disk_default_read
309
        dd      disk_default_write
310
        dd      disk_default_flush
311
        dd      disk_default_adjust_cache_size
2142 serge 312
endg
313
 
314
; =============================================================================
315
; ================================= Functions =================================
316
; =============================================================================
317
 
318
; This function registers a disk device.
319
; This includes:
320
; - allocating an internal structure describing this device;
321
; - registering this structure in the global filesystem.
322
; The function initializes the disk as if there is no media. If a media is
323
; present, the function 'disk_media_changed' should be called after this
324
; function succeeds.
325
; Parameters:
326
; [esp+4] = pointer to DISKFUNC structure with the callbacks
327
; [esp+8] = pointer to name (ASCIIZ string)
328
; [esp+12] = userdata to be passed to the callbacks as is.
329
; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit
330
;            is defined.
331
; Return value:
332
; NULL = operation has failed
333
; non-NULL = handle of the disk. This handle can be used
334
; in the operations with other Disk* functions.
335
; The handle is the pointer to the internal structure DISK.
336
disk_add:
3555 Serge 337
        push    ebx esi         ; save used registers to be stdcall
2142 serge 338
; 1. Allocate the DISK structure.
339
; 1a. Call the heap manager.
3626 Serge 340
        movi    eax, sizeof.DISK
3555 Serge 341
        call    malloc
2142 serge 342
; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0.
3555 Serge 343
        test    eax, eax
344
        jz      .nothing
2268 Serge 345
; 2. Copy the disk name to the DISK structure.
2142 serge 346
; 2a. Get length of the name, including the terminating zero.
3555 Serge 347
        mov     ebx, [esp+8+8]  ; ebx = pointer to name
348
        push    eax             ; save allocated pointer to DISK
349
        xor     eax, eax        ; the argument of malloc() is in eax
2142 serge 350
@@:
3555 Serge 351
        inc     eax
352
        cmp     byte [ebx+eax-1], 0
353
        jnz     @b
3725 Serge 354
; 2b. Call the heap manager.
3555 Serge 355
        call    malloc
2142 serge 356
; 2c. Check the result. If allocation failed, go to 7.
3555 Serge 357
        pop     esi             ; restore allocated pointer to DISK
358
        test    eax, eax
359
        jz      .free
2142 serge 360
; 2d. Store the allocated pointer to the DISK structure.
3555 Serge 361
        mov     [esi+DISK.Name], eax
2142 serge 362
; 2e. Copy the name.
363
@@:
3555 Serge 364
        mov     dl, [ebx]
365
        mov     [eax], dl
366
        inc     ebx
367
        inc     eax
368
        test    dl, dl
369
        jnz     @b
2142 serge 370
; 3. Copy other arguments of the function to the DISK structure.
3555 Serge 371
        mov     eax, [esp+4+8]
372
        mov     [esi+DISK.Functions], eax
373
        mov     eax, [esp+12+8]
374
        mov     [esi+DISK.UserData], eax
375
        mov     eax, [esp+16+8]
376
        mov     [esi+DISK.DriverFlags], eax
2142 serge 377
; 4. Initialize other fields of the DISK structure.
378
; Media is not inserted, reference counter is 1.
3555 Serge 379
        lea     ecx, [esi+DISK.MediaLock]
380
        call    mutex_init
381
        xor     eax, eax
382
        mov     dword [esi+DISK.MediaInserted], eax
3500 Serge 383
        mov     [esi+DISK.MediaRefCount], eax
3555 Serge 384
        inc     eax
385
        mov     [esi+DISK.RefCount], eax
2142 serge 386
; The DISK structure is initialized.
387
; 5. Insert the new structure to the global list.
388
; 5a. Acquire the mutex.
3555 Serge 389
        mov     ecx, disk_list_mutex
390
        call    mutex_lock
2142 serge 391
; 5b. Insert item to the tail of double-linked list.
3555 Serge 392
        mov     edx, disk_list
393
        list_add_tail esi, edx     ;esi= new edx= list head
2142 serge 394
; 5c. Release the mutex.
3555 Serge 395
        call    mutex_unlock
2142 serge 396
; 6. Return with eax = pointer to DISK.
3555 Serge 397
        xchg    eax, esi
398
        jmp     .nothing
2142 serge 399
.free:
400
; Memory allocation for DISK structure succeeded, but for disk name failed.
401
; 7. Free the DISK structure.
3555 Serge 402
        xchg    eax, esi
403
        call    free
2142 serge 404
; 8. Return with eax = 0.
3555 Serge 405
        xor     eax, eax
2142 serge 406
.nothing:
407
; 9. Return.
3555 Serge 408
        pop     esi ebx         ; restore used registers to be stdcall
409
        ret     16              ; purge 4 dword arguments to be stdcall
2142 serge 410
 
411
; This function deletes a disk device from the global filesystem.
412
; This includes:
413
; - removing a media including all partitions;
414
; - deleting this structure from the global filesystem;
415
; - dereferencing the DISK structure and possibly destroying it.
416
; Parameters:
417
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
418
; Return value: none.
419
disk_del:
3555 Serge 420
        push    esi         ; save used registers to be stdcall
2142 serge 421
; 1. Force media to be removed. If the media is already removed, the
422
; call does nothing.
2987 Serge 423
        mov     esi, [esp+4+4]  ; esi = handle of the disk
3555 Serge 424
        stdcall disk_media_changed, esi, 0
2142 serge 425
; 2. Delete the structure from the global list.
426
; 2a. Acquire the mutex.
3555 Serge 427
        mov     ecx, disk_list_mutex
428
        call    mutex_lock
2142 serge 429
; 2b. Delete item from double-linked list.
3555 Serge 430
        mov     eax, [esi+DISK.Next]
431
        mov     edx, [esi+DISK.Prev]
432
        mov     [eax+DISK.Prev], edx
433
        mov     [edx+DISK.Next], eax
2142 serge 434
; 2c. Release the mutex.
3555 Serge 435
        call    mutex_unlock
2142 serge 436
; 3. The structure still has one reference created in disk_add. Remove this
437
; reference. If there are no other references, disk_dereference will free the
438
; structure.
3555 Serge 439
        call    disk_dereference
2142 serge 440
; 4. Return.
3555 Serge 441
        pop     esi             ; restore used registers to be stdcall
442
        ret     4               ; purge 1 dword argument to be stdcall
2142 serge 443
 
444
; This is an internal function which removes a previously obtained reference
445
; to the disk. If this is the last reference, this function lets the driver
446
; finalize all associated data, and afterwards frees the DISK structure.
447
; esi = pointer to DISK structure
448
disk_dereference:
449
; 1. Decrement reference counter. Use atomic operation to correctly handle
450
; possible simultaneous calls.
2434 Serge 451
        lock dec [esi+DISK.RefCount]
2142 serge 452
; 2. If the result is nonzero, there are other references, so nothing to do.
453
; In this case, return (go to 4).
3555 Serge 454
        jnz     .nothing
2142 serge 455
; 3. If we are here, we just removed the last reference and must destroy the
456
; disk object.
457
; 3a. Call the driver.
3555 Serge 458
        mov     al, DISKFUNC.close
459
        stdcall disk_call_driver
2142 serge 460
; 3b. Free the structure.
3555 Serge 461
        xchg    eax, esi
3232 Serge 462
        push    ebx
3555 Serge 463
        call    free
3232 Serge 464
        pop     ebx
2142 serge 465
; 4. Return.
466
.nothing:
3555 Serge 467
        ret
2142 serge 468
 
469
; This is an internal function which removes a previously obtained reference
470
; to the media. If this is the last reference, this function calls 'closemedia'
471
; callback to signal the driver that the processing has finished and it is safe
472
; to inform about a new media.
473
; esi = pointer to DISK structure
474
disk_media_dereference:
475
; 1. Decrement reference counter. Use atomic operation to correctly handle
476
; possible simultaneous calls.
2434 Serge 477
        lock dec [esi+DISK.MediaRefCount]
2142 serge 478
; 2. If the result is nonzero, there are other references, so nothing to do.
479
; In this case, return (go to 4).
3555 Serge 480
        jnz     .nothing
2142 serge 481
; 3. If we are here, we just removed the last reference and must destroy the
482
; media object.
483
; Note that the same place inside the DISK structure is reused for all media
484
; objects, so we must guarantee that reusing does not happen while freeing.
485
; Reusing is only possible when someone processes a new media. There are two
486
; mutually exclusive variants:
487
; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit
488
;   in DISK.DriverFlags is not set). In this case, we require from the driver
489
;   that such notification (except for the first one) can occur only after a
490
;   call to 'closemedia' callback.
491
; * driver does not issue media insert notifications. In this case, the kernel
492
;   itself must sometimes check whether media is inserted. We have the flag
493
;   DISK.MediaUsed, visible to the kernel. This flag signals to the other parts
494
;   of kernel that the way is free.
495
; In the first case other parts of the kernel do not use DISK.MediaUsed, so it
496
; does not matter when this flag is cleared. In the second case this flag must
497
; be cleared after all other actions, including call to 'closemedia'.
498
; 3a. Free all partitions.
3555 Serge 499
        push    esi edi
500
        mov     edi, [esi+DISK.NumPartitions]
501
        mov     esi, [esi+DISK.Partitions]
502
        test    edi, edi
503
        jz      .nofree
2142 serge 504
.freeloop:
3555 Serge 505
        lodsd
3908 Serge 506
        mov     ecx, [eax+PARTITION.FSUserFunctions]
507
        call    dword [ecx]
3555 Serge 508
        dec     edi
509
        jnz     .freeloop
2142 serge 510
.nofree:
3555 Serge 511
        pop     edi esi
2142 serge 512
; 3b. Free the cache.
3555 Serge 513
        call    disk_free_cache
2142 serge 514
; 3c. Call the driver.
3555 Serge 515
        mov     al, DISKFUNC.closemedia
516
        stdcall disk_call_driver
2142 serge 517
; 3d. Clear the flag.
3555 Serge 518
        mov     [esi+DISK.MediaUsed], 0
2142 serge 519
.nothing:
3555 Serge 520
        ret
2142 serge 521
 
522
; This function is called by the driver and informs the kernel that the media
523
; has changed. If the media is non-removable, it is called exactly once
524
; immediately after 'disk_add' and once from 'disk_del'.
525
; Parameters:
526
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
527
; [esp+8] = new status of the media: zero = no media, nonzero = media inserted.
528
disk_media_changed:
3555 Serge 529
        push    ebx esi edi             ; save used registers to be stdcall
2142 serge 530
; 1. Remove the existing media, if it is present.
3555 Serge 531
        mov     esi, [esp+4+12]         ; esi = pointer to DISK
2142 serge 532
; 1a. Check whether it is present. Since DISK.MediaInserted is changed only
533
; in this function and calls to this function are synchronized, no lock is
534
; required for checking.
3555 Serge 535
        cmp     [esi+DISK.MediaInserted], 0
536
        jz      .noremove
2142 serge 537
; We really need to remove the media.
538
; 1b. Acquire mutex.
3555 Serge 539
        lea     ecx, [esi+DISK.MediaLock]
540
        call    mutex_lock
2142 serge 541
; 1c. Clear the flag.
3555 Serge 542
        mov     [esi+DISK.MediaInserted], 0
2142 serge 543
; 1d. Release mutex.
3555 Serge 544
        call    mutex_unlock
2142 serge 545
; 1e. Remove the "lifetime" reference and possibly destroy the structure.
3555 Serge 546
        call    disk_media_dereference
2142 serge 547
.noremove:
548
; 2. Test whether there is new media.
3555 Serge 549
        cmp     dword [esp+8+12], 0
550
        jz      .noinsert
2142 serge 551
; Yep, there is.
552
; 3. Process the new media. We assume that all media fields are available to
553
; use, see comments in 'disk_media_dereference' (this covers using by previous
554
; media referencers) and note that calls to this function are synchronized
555
; (this covers using by new media referencers).
556
; 3a. Call the 'querymedia' callback.
557
; .Flags are set to zero for possible future extensions.
3555 Serge 558
        lea     edx, [esi+DISK.MediaInfo]
559
        and     [edx+DISKMEDIAINFO.Flags], 0
560
        mov     al, DISKFUNC.querymedia
561
        stdcall disk_call_driver, edx
2142 serge 562
; 3b. Check the result of the callback. Abort if it failed.
3555 Serge 563
        test    eax, eax
564
        jnz     .noinsert
2142 serge 565
; 3c. Allocate the cache unless disabled by the driver. Abort if failed.
3555 Serge 566
        call    disk_init_cache
567
        test    al, al
568
        jz      .noinsert
2142 serge 569
; 3d. Acquire the lifetime reference for the media object.
3555 Serge 570
        inc     [esi+DISK.MediaRefCount]
2142 serge 571
; 3e. Scan for partitions. Ignore result; the list of partitions is valid even
572
; on errors.
3555 Serge 573
        call    disk_scan_partitions
2142 serge 574
; 3f. Media is inserted and available for use.
3555 Serge 575
        inc     [esi+DISK.MediaInserted]
2142 serge 576
.noinsert:
577
; 4. Return.
3555 Serge 578
        pop     edi esi ebx             ; restore used registers to be stdcall
579
        ret     8                       ; purge 2 dword arguments to be stdcall
2142 serge 580
 
581
; This function is a thunk for all functions of a disk driver.
582
; It checks whether the referenced function is implemented in the driver.
583
; If so, this function jumps to the function in the driver.
584
; Otherwise, it jumps to the default implementation.
585
; al = offset of function in the DISKFUNC structure;
586
; esi = pointer to the DISK structure;
587
; stack is the same as for the corresponding function except that the
588
; first parameter (void* userdata) is prepended automatically.
589
disk_call_driver:
3555 Serge 590
        movzx   eax, al ; eax = offset of function in the DISKFUNC structure
2142 serge 591
; 1. Prepend the first argument to the stack.
3555 Serge 592
        pop     ecx     ; ecx = return address
593
        push    [esi+DISK.UserData]     ; add argument
594
        push    ecx     ; save return address
2142 serge 595
; 2. Check that the required function is inside the table. If not, go to 5.
3555 Serge 596
        mov     ecx, [esi+DISK.Functions]
597
        cmp     eax, [ecx+DISKFUNC.strucsize]
598
        jae     .default
2142 serge 599
; 3. Check that the required function is implemented. If not, go to 5.
3555 Serge 600
        mov     ecx, [ecx+eax]
601
        test    ecx, ecx
602
        jz      .default
2142 serge 603
; 4. Jump to the required function.
3555 Serge 604
        jmp     ecx
2142 serge 605
.default:
606
; 5. Driver does not implement the required function; use default implementation.
3555 Serge 607
        jmp     dword [disk_default_callbacks+eax-4]
2142 serge 608
 
609
; The default implementation of DISKFUNC.querymedia.
610
disk_default_querymedia:
3626 Serge 611
        movi    eax, DISK_STATUS_INVALID_CALL
3555 Serge 612
        ret     8
2142 serge 613
 
614
; The default implementation of DISKFUNC.read and DISKFUNC.write.
615
disk_default_read:
616
disk_default_write:
3626 Serge 617
        movi    eax, DISK_STATUS_INVALID_CALL
3555 Serge 618
        ret     20
2142 serge 619
 
620
; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and
621
; DISKFUNC.flush.
622
disk_default_close:
623
disk_default_closemedia:
624
disk_default_flush:
3555 Serge 625
        xor     eax, eax
626
        ret     4
2142 serge 627
 
628
; The default implementation of DISKFUNC.adjust_cache_size.
629
disk_default_adjust_cache_size:
3232 Serge 630
        mov     eax, [esp+8]
631
        ret     8
2142 serge 632
 
2268 Serge 633
; This is an internal function called from 'disk_media_changed' when a new media
2142 serge 634
; is detected. It creates the list of partitions for the media.
635
; If media is not partitioned, then the list consists of one partition which
636
; covers all the media.
637
; esi = pointer to the DISK structure.
638
disk_scan_partitions:
639
; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list.
3555 Serge 640
        and     [esi+DISK.NumPartitions], 0
641
        and     [esi+DISK.Partitions], 0
5201 serge 642
; 2. Acquire the buffer for MBR and bootsector tests. See the comment before
2142 serge 643
; the 'partition_buffer_users' variable.
5201 serge 644
        mov     eax, [esi+DISK.MediaInfo.SectorSize]
645
        cmp     eax, 512
646
        jnz     @f
3555 Serge 647
        mov     ebx, mbr_buffer         ; assume the global buffer is free
2434 Serge 648
        lock inc [partition_buffer_users]
3555 Serge 649
        jz      .buffer_acquired        ; yes, it is free
2434 Serge 650
        lock dec [partition_buffer_users]       ; no, we must allocate
5201 serge 651
@@:
652
        lea     eax, [eax*3]
653
        stdcall kernel_alloc, eax
3555 Serge 654
        test    eax, eax
655
        jz      .nothing
656
        xchg    eax, ebx
2142 serge 657
.buffer_acquired:
658
; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no
659
; more than MAX_NUM_PARTITION times.
5201 serge 660
; 3. Prepare things for the loop.
2142 serge 661
; ebp will hold the sector number for current MBR/EBR.
662
; [esp] will hold the sector number for current extended partition, if there
663
; is one.
664
; [esp+4] will hold the counter that prevents long loops.
3555 Serge 665
        push    ebp             ; save ebp
666
        push    MAX_NUM_PARTITIONS      ; the counter of max MBRs to process
667
        xor     ebp, ebp        ; start from sector zero
668
        push    ebp             ; no extended partition yet
5201 serge 669
; 4. MBR is 512 bytes long. If sector size is less than 512 bytes,
670
; assume no MBR, no partitions and go to 10.
671
        cmp     [esi+DISK.MediaInfo.SectorSize], 512
672
        jb      .notmbr
2142 serge 673
.new_mbr:
674
; 5. Read the current sector.
675
; Note that 'read' callback operates with 64-bit sector numbers, so we must
676
; push additional zero as a high dword of sector number.
3555 Serge 677
        mov     al, DISKFUNC.read
678
        push    1
679
        stdcall disk_call_driver, ebx, ebp, 0, esp
680
        pop     ecx
2142 serge 681
; 6. If the read has failed, abort the loop.
3555 Serge 682
        dec     ecx
683
        jnz     .mbr_failed
2142 serge 684
; 7. Check the MBR/EBR signature. If it is wrong, abort the loop.
685
; Soon we will access the partition table which starts at ebx+0x1BE,
686
; so we can fill its address right now. If we do it now, then the addressing
687
; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset.
3555 Serge 688
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
689
        cmp     word [ecx+0x40], 0xaa55
690
        jnz     .mbr_failed
2142 serge 691
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to
692
; execute step 9 and possibly step 10.
3555 Serge 693
        test    ebp, ebp
694
        jnz     .mbr
2268 Serge 695
; The partition table can be present or not present. In the first case, we just
696
; read the MBR. In the second case, we just read the bootsector for a
2142 serge 697
; filesystem.
2268 Serge 698
; The following algorithm is used to distinguish between these cases.
2142 serge 699
; A. If at least one entry of the partition table is invalid, this is
700
;    a bootsector. See the description of 'is_partition_table_entry' for
701
;    definition of validity.
702
; B. If all entries are empty (filesystem type field is zero) and the first
703
;    byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to
704
;    have zeros in the place of partition table.
2268 Serge 705
; C. Otherwise, this is an MBR.
2142 serge 706
; 9. Test for MBR vs bootsector.
707
; 9a. Check entries. If any is invalid, go to 10 (rule A).
3555 Serge 708
        call    is_partition_table_entry
709
        jc      .notmbr
710
        add     ecx, 10h
711
        call    is_partition_table_entry
712
        jc      .notmbr
713
        add     ecx, 10h
714
        call    is_partition_table_entry
715
        jc      .notmbr
716
        add     ecx, 10h
717
        call    is_partition_table_entry
718
        jc      .notmbr
2142 serge 719
; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C).
3555 Serge 720
        mov     al, [ecx-30h+PARTITION_TABLE_ENTRY.Type]
721
        or      al, [ecx-20h+PARTITION_TABLE_ENTRY.Type]
722
        or      al, [ecx-10h+PARTITION_TABLE_ENTRY.Type]
723
        or      al, [ecx+PARTITION_TABLE_ENTRY.Type]
724
        jnz     .mbr
2142 serge 725
; 9c. Empty partition table or bootsector with many zeroes? (rule B)
3555 Serge 726
        cmp     byte [ebx], 0EBh
727
        jz      .notmbr
728
        cmp     byte [ebx], 0E9h
729
        jnz     .mbr
2142 serge 730
.notmbr:
2268 Serge 731
; 10. This is not an  MBR. The media is not partitioned. Create one partition
2142 serge 732
; which covers all the media and abort the loop.
3555 Serge 733
        stdcall disk_add_partition, 0, 0, \
3908 Serge 734
                dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4], esi
3555 Serge 735
        jmp     .done
2142 serge 736
.mbr:
737
; 11. Process all entries of the new MBR/EBR
3555 Serge 738
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
739
        push    0       ; assume no extended partition
740
        call    process_partition_table_entry
741
        add     ecx, 10h
742
        call    process_partition_table_entry
743
        add     ecx, 10h
744
        call    process_partition_table_entry
745
        add     ecx, 10h
746
        call    process_partition_table_entry
747
        pop     ebp
2142 serge 748
; 12. Test whether we found a new EBR and should continue the loop.
749
; 12a. If there was no next EBR, return.
3555 Serge 750
        test    ebp, ebp
751
        jz      .done
2142 serge 752
; Ok, we have EBR.
753
; 12b. EBRs addresses are relative to the start of extended partition.
754
; For simplicity, just abort if an 32-bit overflow occurs; large disks
755
; are most likely partitioned with GPT, not MBR scheme, since the precise
756
; calculation here would increase limit just twice at the price of big
757
; compatibility problems.
3555 Serge 758
        pop     eax     ; load extended partition
759
        add     ebp, eax
760
        jc      .mbr_failed
2142 serge 761
; 12c. If extended partition has not yet started, start it.
3555 Serge 762
        test    eax, eax
763
        jnz     @f
764
        mov     eax, ebp
2142 serge 765
@@:
766
; 12c. If the limit is not exceeded, continue the loop.
3555 Serge 767
        dec     dword [esp]
768
        push    eax     ; store extended partition
769
        jnz     .new_mbr
2142 serge 770
.mbr_failed:
771
.done:
772
; 13. Cleanup after the loop.
3555 Serge 773
        pop     eax     ; not important anymore
774
        pop     eax     ; not important anymore
775
        pop     ebp     ; restore ebp
2142 serge 776
; 14. Release the buffer.
777
; 14a. Test whether it is the global buffer or we have allocated it.
3555 Serge 778
        cmp     ebx, mbr_buffer
779
        jz      .release_partition_buffer
2142 serge 780
; 14b. If we have allocated it, free it.
3555 Serge 781
        xchg    eax, ebx
782
        call    free
783
        jmp     .nothing
2142 serge 784
; 14c. Otherwise, release reference.
785
.release_partition_buffer:
2434 Serge 786
        lock dec [partition_buffer_users]
2142 serge 787
.nothing:
788
; 15. Return.
3555 Serge 789
        ret
2142 serge 790
 
791
; This is an internal function called from disk_scan_partitions. It checks
792
; whether the entry pointed to by ecx is a valid entry of partition table.
793
; The entry is valid if the first byte is 0 or 80h, the first sector plus the
794
; length is less than twice the size of media. Multiplication by two is
795
; required since the size mentioned in the partition table can be slightly
796
; greater than the real size.
797
is_partition_table_entry:
798
; 1. Check .Bootable field.
3555 Serge 799
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Bootable]
800
        and     al, 7Fh
801
        jnz     .invalid
2142 serge 802
; 3. Calculate first sector + length. Note that .FirstAbsSector is relative
803
; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length.
3555 Serge 804
        mov     eax, ebp
805
        xor     edx, edx
806
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
807
        adc     edx, 0
808
        add     eax, [ecx+PARTITION_TABLE_ENTRY.Length]
809
        adc     edx, 0
2142 serge 810
; 4. Divide by two.
3555 Serge 811
        shr     edx, 1
812
        rcr     eax, 1
2142 serge 813
; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not
814
; overflow, this is bad.
3555 Serge 815
        sub     eax, dword [esi+DISK.MediaInfo.Capacity]
816
        sbb     edx, dword [esi+DISK.MediaInfo.Capacity+4]
817
        jnc     .invalid
2142 serge 818
.valid:
819
; 5. Return success: CF is cleared.
3555 Serge 820
        clc
821
        ret
2142 serge 822
.invalid:
823
; 6. Return fail: CF is set.
3555 Serge 824
        stc
825
        ret
2142 serge 826
 
827
; This is an internal function called from disk_scan_partitions. It processes
828
; the entry pointed to by ecx.
829
; * If the entry is invalid, just ignore this entry.
830
; * If the type is zero, just ignore this entry.
831
; * If the type is one of types for extended partition, store the address
832
;   of this partition as the new MBR in [esp+4].
833
; * Otherwise, add the partition to the list of partitions for this disk.
834
;   We don't use the type from the entry to identify the file system;
835
;   fs-specific checks do this more reliably.
836
process_partition_table_entry:
837
; 1. Check for valid entry. If invalid, return (go to 5).
3555 Serge 838
        call    is_partition_table_entry
839
        jc      .nothing
2142 serge 840
; 2. Check for empty entry. If invalid, return (go to 5).
3555 Serge 841
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Type]
842
        test    al, al
843
        jz      .nothing
2142 serge 844
; 3. Check for extended partition. If extended, go to 6.
845
irp type,\
3555 Serge 846
    0x05,\                 ; DOS: extended partition
847
    0x0f,\                 ; WIN95: extended partition, LBA-mapped
848
    0xc5,\                 ; DRDOS/secured: extended partition
849
    0xd5                   ; Old Multiuser DOS secured: extended partition
2142 serge 850
{
3555 Serge 851
        cmp     al, type
852
        jz      .extended
2142 serge 853
}
854
; 4. If we are here, that is a normal partition. Add it to the list.
855
; Note that the first sector is relative to MBR/EBR.
3555 Serge 856
        mov     eax, ebp
857
        xor     edx, edx
858
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
859
        adc     edx, 0
860
        push    ecx
861
        stdcall disk_add_partition, eax, edx, \
3908 Serge 862
                [ecx+PARTITION_TABLE_ENTRY.Length], 0, esi
3555 Serge 863
        pop     ecx
2142 serge 864
.nothing:
865
; 5. Return.
3555 Serge 866
        ret
2142 serge 867
.extended:
868
; 6. If we are here, that is an extended partition. Store the address.
3555 Serge 869
        mov     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
870
        mov     [esp+4], eax
871
        ret
2142 serge 872
 
873
; This is an internal function called from disk_scan_partitions and
874
; process_partition_table_entry. It adds one partition to the list of
875
; partitions for the media.
3908 Serge 876
; Important note: start, length, disk MUST be present and
877
; MUST be in the same order as in PARTITION structure.
878
; esi duplicates [disk].
879
proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword, disk:dword
2142 serge 880
; 1. Check that this partition will not exceed the limit on total number.
3555 Serge 881
        cmp     [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS
882
        jae     .nothing
2142 serge 883
; 2. Check that this partition does not overlap with any already registered
884
; partition. Since any file system assumes that the disk data will not change
885
; outside of its control, such overlap could be destructive.
886
; Since the number of partitions is usually very small and is guaranteed not
887
; to be large, the simple linear search is sufficient.
888
; 2a. Prepare the loop: edi will point to the current item of .Partitions
889
; array, ecx will be the current item, ebx will hold number of items left.
3555 Serge 890
        mov     edi, [esi+DISK.Partitions]
891
        mov     ebx, [esi+DISK.NumPartitions]
892
        test    ebx, ebx
893
        jz      .partitionok
2142 serge 894
.scan_existing:
895
; 2b. Get the next partition.
3555 Serge 896
        mov     ecx, [edi]
897
        add     edi, 4
2142 serge 898
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to
899
; the left of [start, start+length) or entirely to the right.
900
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between
901
; cases "to the left" (2e) and "to the right" (2d).
3555 Serge 902
        mov     eax, dword [ecx+PARTITION.FirstSector]
903
        mov     edx, dword [ecx+PARTITION.FirstSector+4]
904
        sub     eax, dword [start]
905
        sbb     edx, dword [start+4]
906
        jb      .less
2142 serge 907
; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector
908
; is greater than or equal to start+length; the subtraction
909
; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is
910
; good or to 2f in the other case.
3555 Serge 911
        sub     eax, dword [length]
912
        sbb     edx, dword [length+4]
913
        jb      .overlap
914
        jmp     .next_existing
2142 serge 915
.less:
916
; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less
917
; than or equal to start. If the addition (.FirstSector-start) + .Length does
918
; not cause overflow, then .FirstSector + .Length is strictly less than start;
919
; since the equality is also valid, use decrement preliminarily. Go to 2g or
920
; 2f depending on the overflow.
3555 Serge 921
        sub     eax, 1
922
        sbb     edx, 0
923
        add     eax, dword [ecx+PARTITION.Length]
924
        adc     edx, dword [ecx+PARTITION.Length+4]
925
        jnc     .next_existing
2142 serge 926
.overlap:
927
; 2f. The partition overlaps with previously registered partition. Say warning
928
; and return with nothing done.
3555 Serge 929
        dbgstr 'two partitions overlap, ignoring the last one'
930
        jmp     .nothing
2142 serge 931
.next_existing:
932
; 2g. The partition does not overlap with the current partition. Continue the
933
; loop.
3555 Serge 934
        dec     ebx
935
        jnz     .scan_existing
2142 serge 936
.partitionok:
937
; 3. The partition has passed tests. Reallocate the partitions array for a new
938
; entry.
939
; 3a. Call the allocator.
3555 Serge 940
        mov     eax, [esi+DISK.NumPartitions]
941
        inc     eax     ; one more entry
942
        shl     eax, 2  ; each entry is dword
943
        call    malloc
2142 serge 944
; 3b. Test the result. If failed, return with nothing done.
3555 Serge 945
        test    eax, eax
946
        jz      .nothing
2142 serge 947
; 3c. Copy the old array to the new array.
3555 Serge 948
        mov     edi, eax
949
        push    esi
950
        mov     ecx, [esi+DISK.NumPartitions]
951
        mov     esi, [esi+DISK.Partitions]
952
        rep movsd
953
        pop     esi
2142 serge 954
; 3d. Set the field in the DISK structure to the new array.
3555 Serge 955
        xchg    [esi+DISK.Partitions], eax
2142 serge 956
; 3e. Free the old array.
3555 Serge 957
        call    free
2142 serge 958
; 4. Recognize the file system.
959
; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure
960
; with possible filesystem-specific fields.
3555 Serge 961
        call    disk_detect_partition
2142 serge 962
; 4b. Check return value. If zero, return with list not changed; so far only
963
; the array was reallocated, this is ok for other code.
3555 Serge 964
        test    eax, eax
965
        jz      .nothing
2142 serge 966
; 5. Insert the new partition to the list.
3555 Serge 967
        stosd
968
        inc     [esi+DISK.NumPartitions]
2142 serge 969
; 6. Return.
970
.nothing:
3555 Serge 971
        ret
2142 serge 972
endp
973
 
974
; This is an internal function called from disk_add_partition.
975
; It tries to recognize the file system on the partition and allocates the
976
; corresponding PARTITION structure with filesystem-specific fields.
977
disk_detect_partition:
978
; This function inherits the stack frame from disk_add_partition. In stdcall
979
; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp
980
; and [ebp+4]=return address.
981
virtual at ebp+8
3555 Serge 982
.start  dq      ?
983
.length dq      ?
3908 Serge 984
.disk   dd      ?
2142 serge 985
end virtual
3908 Serge 986
; 1. Read the bootsector to the buffer.
2987 Serge 987
; When disk_add_partition is called, ebx contains a pointer to
3908 Serge 988
; a three-sectors-sized buffer. This function saves ebx in the stack
2987 Serge 989
; immediately before ebp.
3908 Serge 990
        mov     ebx, [ebp-4] ; get buffer
5201 serge 991
        add     ebx, [esi+DISK.MediaInfo.SectorSize] ; advance over MBR data to bootsector data
3908 Serge 992
        add     ebp, 8       ; ebp points to part of PARTITION structure
993
        xor     eax, eax     ; first sector of the partition
994
        call    fs_read32_sys
995
        push    eax
2987 Serge 996
; 2. Run tests for all supported filesystems. If at least one test succeeded,
997
; go to 4.
3908 Serge 998
; For tests:
999
; ebp -> first three fields of PARTITION structure, .start, .length, .disk;
1000
; [esp] = error code after bootsector read: 0 = ok, otherwise = failed,
1001
; ebx points to the buffer for bootsector,
5201 serge 1002
; ebx+[esi+DISK.MediaInfo.SectorSize] points to sector-sized buffer that can be used for anything.
2987 Serge 1003
        call    fat_create_partition
1004
        test    eax, eax
1005
        jnz     .success
3908 Serge 1006
        call    ntfs_create_partition
1007
        test    eax, eax
1008
        jnz     .success
1009
        call    ext2_create_partition
1010
        test    eax, eax
1011
        jnz     .success
4265 Serge 1012
        call    xfs_create_partition
1013
        test    eax, eax
1014
        jnz     .success
2987 Serge 1015
; 3. No file system has recognized the volume, so just allocate the PARTITION
2142 serge 1016
; structure without extra fields.
3626 Serge 1017
        movi    eax, sizeof.PARTITION
3555 Serge 1018
        call    malloc
1019
        test    eax, eax
1020
        jz      .nothing
3908 Serge 1021
        mov     edx, dword [ebp+PARTITION.FirstSector]
3555 Serge 1022
        mov     dword [eax+PARTITION.FirstSector], edx
3908 Serge 1023
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
3555 Serge 1024
        mov     dword [eax+PARTITION.FirstSector+4], edx
3908 Serge 1025
        mov     edx, dword [ebp+PARTITION.Length]
3555 Serge 1026
        mov     dword [eax+PARTITION.Length], edx
3908 Serge 1027
        mov     edx, dword [ebp+PARTITION.Length+4]
3555 Serge 1028
        mov     dword [eax+PARTITION.Length+4], edx
2987 Serge 1029
        mov     [eax+PARTITION.Disk], esi
3908 Serge 1030
        mov     [eax+PARTITION.FSUserFunctions], default_fs_functions
2987 Serge 1031
.success:
2142 serge 1032
.nothing:
3908 Serge 1033
        sub     ebp, 8 ; restore ebp
2987 Serge 1034
; 4. Return with eax = pointer to PARTITION or NULL.
1035
        pop     ecx
3555 Serge 1036
        ret
2142 serge 1037
 
3908 Serge 1038
iglobal
1039
align 4
1040
default_fs_functions:
1041
        dd      free
1042
        dd      0       ; no user functions
1043
endg
1044
 
2142 serge 1045
; This function is called from file_system_lfn.
1046
; This handler gets the control each time when fn 70 is called
1047
; with unknown item of root subdirectory.
1048
; in: esi -> name
1049
;     ebp = 0 or rest of name relative to esi
1050
; out: if the handler processes path, it must not return in file_system_lfn,
1051
;      but instead pop return address and return directly to the caller
1052
;      otherwise simply return
1053
dyndisk_handler:
3555 Serge 1054
        push    ebx edi         ; save registers used in file_system_lfn
2142 serge 1055
; 1. Acquire the mutex.
3555 Serge 1056
        mov     ecx, disk_list_mutex
1057
        call    mutex_lock
2142 serge 1058
; 2. Loop over the list of DISK structures.
1059
; 2a. Initialize.
3555 Serge 1060
        mov     ebx, disk_list
2142 serge 1061
.scan:
1062
; 2b. Get the next item.
3555 Serge 1063
        mov     ebx, [ebx+DISK.Next]
2142 serge 1064
; 2c. Check whether the list is done. If so, go to 3.
3555 Serge 1065
        cmp     ebx, disk_list
1066
        jz      .notfound
2142 serge 1067
; 2d. Compare names. If names match, go to 5.
3555 Serge 1068
        mov     edi, [ebx+DISK.Name]
1069
        push    esi
2142 serge 1070
@@:
1071
; esi points to the name from fs operation; it is terminated by zero or slash.
3555 Serge 1072
        lodsb
1073
        test    al, al
1074
        jz      .eoin_dec
1075
        cmp     al, '/'
1076
        jz      .eoin
2142 serge 1077
; edi points to the disk name.
3555 Serge 1078
        inc     edi
2142 serge 1079
; edi points to lowercase name, this is a requirement for the driver.
1080
; Characters at esi can have any register. Lowercase the current character.
1081
; This lowercasing works for latin letters and digits; since the disk name
1082
; should not contain other symbols, this is ok.
3555 Serge 1083
        or      al, 20h
1084
        cmp     al, [edi-1]
1085
        jz      @b
2142 serge 1086
.wrongname:
1087
; 2f. Names don't match. Continue the loop.
3555 Serge 1088
        pop     esi
1089
        jmp     .scan
2142 serge 1090
.notfound:
1091
; The loop is done and no name matches.
1092
; 3. Release the mutex.
3555 Serge 1093
        call    mutex_unlock
2142 serge 1094
; 4. Return normally.
3555 Serge 1095
        pop     edi ebx         ; restore registers used in file_system_lfn
1096
        ret
2142 serge 1097
; part of 2d: the name matches partially, but we must check that this is full
1098
; equality.
1099
.eoin_dec:
3555 Serge 1100
        dec     esi
2142 serge 1101
.eoin:
3555 Serge 1102
        cmp     byte [edi], 0
1103
        jnz     .wrongname
2142 serge 1104
; We found the addressed DISK structure.
1105
; 5. Reference the disk.
2434 Serge 1106
        lock inc [ebx+DISK.RefCount]
2142 serge 1107
; 6. Now we are sure that the DISK structure is not going to die at least
1108
; while we are working with it, so release the global mutex.
3555 Serge 1109
        call    mutex_unlock
2987 Serge 1110
        pop     ecx             ; pop from the stack saved value of esi
2142 serge 1111
; 7. Acquire the mutex for media object.
3555 Serge 1112
        pop     edi             ; restore edi
1113
        lea     ecx, [ebx+DISK.MediaLock]
1114
        call    mutex_lock
2142 serge 1115
; 8. Get the media object. If it is not NULL, reference it.
3555 Serge 1116
        xor     edx, edx
1117
        cmp     [ebx+DISK.MediaInserted], dl
1118
        jz      @f
1119
        mov     edx, ebx
1120
        inc     [ebx+DISK.MediaRefCount]
2142 serge 1121
@@:
1122
; 9. Now we are sure that the media object, if it exists, is not going to die
1123
; at least while we are working with it, so release the mutex for media object.
3555 Serge 1124
        call    mutex_unlock
1125
        mov     ecx, ebx
1126
        pop     ebx eax         ; restore ebx, pop return address
2142 serge 1127
; 10. Check whether the fs operation wants to enumerate partitions (go to 11)
1128
; or work with some concrete partition (go to 12).
3555 Serge 1129
        cmp     byte [esi], 0
1130
        jnz     .haspartition
2142 serge 1131
; 11. The fs operation wants to enumerate partitions.
1132
; 11a. Only "list directory" operation is applicable to / path. Check
1133
; the operation code. If wrong, go to 13.
3555 Serge 1134
        cmp     dword [ebx], 1
1135
        jnz     .access_denied
2142 serge 1136
; 11b. If the media is inserted, use 'fs_dyndisk_next' as an enumeration
1137
; procedure. Otherwise, use 'fs_dyndisk_next_nomedia'.
3555 Serge 1138
        mov     esi, fs_dyndisk_next_nomedia
1139
        test    edx, edx
1140
        jz      @f
1141
        mov     esi, fs_dyndisk_next
2142 serge 1142
@@:
1143
; 11c. Let the procedure from fs_lfn.inc do the job.
3555 Serge 1144
        jmp     file_system_lfn.maindir_noesi
2142 serge 1145
.haspartition:
1146
; 12. The fs operation has specified some partition.
1147
; 12a. Store parameters for callback functions.
3555 Serge 1148
        push    edx
1149
        push    ecx
2142 serge 1150
; 12b. Store callback functions.
3555 Serge 1151
        push    dyndisk_cleanup
1152
        push    fs_dyndisk
1153
        mov     edi, esp
2142 serge 1154
; 12c. Let the procedure from fs_lfn.inc do the job.
3555 Serge 1155
        jmp     file_system_lfn.found2
2142 serge 1156
.access_denied:
1157
; 13. Fail the operation with the appropriate code.
3555 Serge 1158
        mov     dword [esp+32], ERROR_ACCESS_DENIED
2142 serge 1159
.cleanup:
1160
; 14. Cleanup.
3555 Serge 1161
        mov     esi, ecx        ; disk*dereference assume that esi points to DISK
2142 serge 1162
.cleanup_esi:
3555 Serge 1163
        test    edx, edx        ; if there are no media, we didn't reference it
1164
        jz      @f
1165
        call    disk_media_dereference
2142 serge 1166
@@:
3555 Serge 1167
        call    disk_dereference
2142 serge 1168
; 15. Return.
3555 Serge 1169
        ret
2142 serge 1170
 
1171
; This is a callback for cleaning up things called from file_system_lfn.found2.
1172
dyndisk_cleanup:
3555 Serge 1173
        mov     esi, [edi+8]
1174
        mov     edx, [edi+12]
1175
        jmp     dyndisk_handler.cleanup_esi
2142 serge 1176
 
1177
; This is a callback for enumerating partitions called from
1178
; file_system_lfn.maindir in the case of inserted media.
1179
; It just increments eax until DISK.NumPartitions reached and then
1180
; cleans up.
1181
fs_dyndisk_next:
3555 Serge 1182
        cmp     eax, [ecx+DISK.NumPartitions]
1183
        jae     .nomore
1184
        inc     eax
1185
        clc
1186
        ret
2142 serge 1187
.nomore:
3555 Serge 1188
        pusha
1189
        mov     esi, ecx
1190
        call    disk_media_dereference
1191
        call    disk_dereference
1192
        popa
1193
        stc
1194
        ret
2142 serge 1195
 
1196
; This is a callback for enumerating partitions called from
1197
; file_system_lfn.maindir in the case of missing media.
1198
; In this case we create one pseudo-partition.
1199
fs_dyndisk_next_nomedia:
3555 Serge 1200
        cmp     eax, 1
1201
        jae     .nomore
1202
        inc     eax
1203
        clc
1204
        ret
2142 serge 1205
.nomore:
3555 Serge 1206
        pusha
1207
        mov     esi, ecx
1208
        call    disk_dereference
1209
        popa
1210
        stc
1211
        ret
2142 serge 1212
 
1213
; This is a callback for doing real work with selected partition.
1214
; Currently this is just placeholder, since no file systems are supported.
1215
; edi = esp -> {dd fs_dyndisk, dd dyndisk_cleanup, dd pointer to DISK, dd media object}
1216
; ecx = partition number, esi+ebp = ASCIIZ name
1217
fs_dyndisk:
3555 Serge 1218
        dec     ecx     ; convert to zero-based partition index
4287 Serge 1219
        pop     edx edx edx ; edx = pointer to DISK, dword [esp] = NULL or edx
1220
; If the driver does not support insert notifications and we are the only fs
1221
; operation with this disk, ask the driver whether the media
1222
; was inserted/removed/changed. Otherwise, assume that media status is valid.
1223
        test    byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
1224
        jz      .media_accurate
1225
        push    ecx esi
1226
        mov     esi, edx
1227
        cmp     dword [esp+8], 0
1228
        jz      .test_no_media
1229
        cmp     [esi+DISK.MediaRefCount], 2
1230
        jnz     .media_accurate_pop
1231
        lea     edx, [esi+DISK.MediaInfo]
1232
        and     [edx+DISKMEDIAINFO.Flags], 0
1233
        mov     al, DISKFUNC.querymedia
1234
        stdcall disk_call_driver, edx
3555 Serge 1235
        test    eax, eax
4287 Serge 1236
        jz      .media_accurate_pop
1237
        stdcall disk_media_dereference  ; drop our reference so that disk_media_changed could close the media
1238
        stdcall disk_media_changed, esi, 0
1239
        and     dword [esp+8], 0        ; no media
1240
.test_no_media:
1241
        stdcall disk_media_changed, esi, 1      ; issue fake notification
1242
                ; if querymedia() inside disk_media_changed returns error, the notification is ignored
1243
        cmp     [esi+DISK.MediaInserted], 0
1244
        jz      .media_accurate_pop
1245
        lock inc [esi+DISK.MediaRefCount]
1246
        mov     dword [esp+8], esi
1247
.media_accurate_pop:
1248
        mov     edx, esi
1249
        pop     esi ecx
1250
.media_accurate:
1251
        pop     eax
1252
        test    eax, eax
3555 Serge 1253
        jz      .nomedia
2142 serge 1254
.main:
3555 Serge 1255
        cmp     ecx, [edx+DISK.NumPartitions]
1256
        jae     .notfound
2987 Serge 1257
        mov     eax, [edx+DISK.Partitions]
1258
        mov     eax, [eax+ecx*4]
1259
        mov     edi, [eax+PARTITION.FSUserFunctions]
1260
        mov     ecx, [ebx]
3908 Serge 1261
        cmp     [edi+4], ecx
2987 Serge 1262
        jbe     .unsupported
1263
        push    edx
1264
        push    ebp
1265
        mov     ebp, eax
3908 Serge 1266
        call    dword [edi+8+ecx*4]
2987 Serge 1267
        pop     ebp
1268
        pop     edx
1269
        mov     dword [esp+32], eax
1270
        mov     dword [esp+20], ebx
2142 serge 1271
.cleanup:
3555 Serge 1272
        mov     esi, edx
1273
        call    disk_media_dereference
1274
        call    disk_dereference
1275
        ret
2987 Serge 1276
.nofs:
1277
        mov     dword [esp+32], ERROR_UNKNOWN_FS
1278
        jmp     .cleanup
2142 serge 1279
.notfound:
3555 Serge 1280
        mov     dword [esp+32], ERROR_FILE_NOT_FOUND
1281
        jmp     .cleanup
2987 Serge 1282
.unsupported:
3908 Serge 1283
        cmp     edi, default_fs_functions
1284
        jz      .nofs
2987 Serge 1285
        mov     dword [esp+32], ERROR_UNSUPPORTED_FS
1286
        jmp     .cleanup
2142 serge 1287
.nomedia:
3555 Serge 1288
        test    ecx, ecx
1289
        jnz     .notfound
1290
        mov     dword [esp+32], ERROR_DEVICE
1291
        mov     esi, edx
1292
        call    disk_dereference
1293
        ret
2142 serge 1294
 
1295
; This function is called from file_system_lfn.
1296
; This handler is called when virtual root is enumerated
1297
; and must return all items which can be handled by this.
1298
; It is called several times, first time with eax=0
1299
; in: eax = 0 for first call, previously returned value for subsequent calls
1300
; out: eax = 0 => no more items
1301
;      eax != 0 => buffer pointed to by edi contains name of item
1302
dyndisk_enum_root:
3555 Serge 1303
        push    edx             ; save register used in file_system_lfn
1304
        mov     ecx, disk_list_mutex    ; it will be useful
2142 serge 1305
; 1. If this is the first call, acquire the mutex and initialize.
3555 Serge 1306
        test    eax, eax
1307
        jnz     .notfirst
1308
        call    mutex_lock
1309
        mov     eax, disk_list
2142 serge 1310
.notfirst:
1311
; 2. Get next item.
3555 Serge 1312
        mov     eax, [eax+DISK.Next]
2142 serge 1313
; 3. If there are no more items, go to 6.
3555 Serge 1314
        cmp     eax, disk_list
1315
        jz      .last
2142 serge 1316
; 4. Copy name from the DISK structure to edi.
3555 Serge 1317
        push    eax esi
1318
        mov     esi, [eax+DISK.Name]
2142 serge 1319
@@:
3555 Serge 1320
        lodsb
1321
        stosb
1322
        test    al, al
1323
        jnz     @b
1324
        pop     esi eax
2142 serge 1325
; 5. Return with eax = item.
3555 Serge 1326
        pop     edx             ; restore register used in file_system_lfn
1327
        ret
2142 serge 1328
.last:
1329
; 6. Release the mutex and return with eax = 0.
3555 Serge 1330
        call    mutex_unlock
1331
        xor     eax, eax
1332
        pop     edx             ; restore register used in file_system_lfn
1333
        ret