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