Subversion Repositories Kolibri OS

Rev

Rev 2119 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

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