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