Subversion Repositories Kolibri OS

Rev

Rev 9941 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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