Subversion Repositories Kolibri OS

Rev

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

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