Subversion Repositories Kolibri OS

Rev

Rev 2120 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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