]> git.proxmox.com Git - mirror_edk2.git/blame - FatPkg/EnhancedFatDxe/Misc.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / FatPkg / EnhancedFatDxe / Misc.c
CommitLineData
cae7420b
DB
1/** @file\r
2 Miscellaneous functions.\r
b9ec9330 3\r
e38f26a2 4Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>\r
eb6cb4ce 5SPDX-License-Identifier: BSD-2-Clause-Patent\r
b9ec9330
QH
6\r
7\r
cae7420b 8**/\r
b9ec9330 9\r
cae7420b
DB
10#include "Fat.h"\r
11UINT8 mMonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
b9ec9330 12\r
cae7420b 13/**\r
b9ec9330 14\r
cae7420b 15 Create the task\r
b9ec9330 16\r
cae7420b
DB
17 @param IFile - The instance of the open file.\r
18 @param Token - A pointer to the token associated with the transaction.\r
b9ec9330 19\r
cae7420b 20 @return FAT_TASK * - Return the task instance.\r
b9ec9330 21\r
cae7420b 22**/\r
149d6335
RN
23FAT_TASK *\r
24FatCreateTask (\r
bcdcc416
MK
25 FAT_IFILE *IFile,\r
26 EFI_FILE_IO_TOKEN *Token\r
149d6335 27 )\r
149d6335 28{\r
bcdcc416 29 FAT_TASK *Task;\r
149d6335
RN
30\r
31 Task = AllocateZeroPool (sizeof (*Task));\r
32 if (Task != NULL) {\r
33 Task->Signature = FAT_TASK_SIGNATURE;\r
34 Task->IFile = IFile;\r
35 Task->FileIoToken = Token;\r
36 InitializeListHead (&Task->Subtasks);\r
37 InitializeListHead (&Task->Link);\r
38 }\r
bcdcc416 39\r
149d6335
RN
40 return Task;\r
41}\r
42\r
cae7420b 43/**\r
149d6335 44\r
cae7420b 45 Destroy the task.\r
149d6335 46\r
cae7420b 47 @param Task - The task to be destroyed.\r
149d6335 48\r
149d6335 49**/\r
cae7420b
DB
50VOID\r
51FatDestroyTask (\r
bcdcc416 52 FAT_TASK *Task\r
cae7420b 53 )\r
149d6335 54{\r
bcdcc416
MK
55 LIST_ENTRY *Link;\r
56 FAT_SUBTASK *Subtask;\r
149d6335
RN
57\r
58 Link = GetFirstNode (&Task->Subtasks);\r
59 while (!IsNull (&Task->Subtasks, Link)) {\r
60 Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
bcdcc416 61 Link = FatDestroySubtask (Subtask);\r
149d6335 62 }\r
bcdcc416 63\r
149d6335
RN
64 FreePool (Task);\r
65}\r
66\r
cae7420b 67/**\r
149d6335
RN
68\r
69 Wait all non-blocking requests complete.\r
70\r
cae7420b 71 @param IFile - The instance of the open file.\r
149d6335 72\r
149d6335 73**/\r
cae7420b
DB
74VOID\r
75FatWaitNonblockingTask (\r
bcdcc416 76 FAT_IFILE *IFile\r
cae7420b 77 )\r
149d6335 78{\r
bcdcc416 79 BOOLEAN TaskQueueEmpty;\r
149d6335
RN
80\r
81 do {\r
82 EfiAcquireLock (&FatTaskLock);\r
83 TaskQueueEmpty = IsListEmpty (&IFile->Tasks);\r
84 EfiReleaseLock (&FatTaskLock);\r
85 } while (!TaskQueueEmpty);\r
86}\r
87\r
cae7420b 88/**\r
149d6335
RN
89\r
90 Remove the subtask from subtask list.\r
91\r
cae7420b 92 @param Subtask - The subtask to be removed.\r
149d6335 93\r
cae7420b 94 @return LIST_ENTRY * - The next node in the list.\r
149d6335 95\r
cae7420b
DB
96**/\r
97LIST_ENTRY *\r
98FatDestroySubtask (\r
bcdcc416 99 FAT_SUBTASK *Subtask\r
cae7420b 100 )\r
149d6335 101{\r
bcdcc416 102 LIST_ENTRY *Link;\r
149d6335
RN
103\r
104 gBS->CloseEvent (Subtask->DiskIo2Token.Event);\r
105\r
106 Link = RemoveEntryList (&Subtask->Link);\r
107 FreePool (Subtask);\r
108\r
109 return Link;\r
110}\r
111\r
cae7420b
DB
112/**\r
113\r
114 Execute the task.\r
115\r
116 @param IFile - The instance of the open file.\r
117 @param Task - The task to be executed.\r
118\r
44c9618a 119 @retval EFI_SUCCESS - The task was executed successfully.\r
cae7420b
DB
120 @return other - An error occurred when executing the task.\r
121\r
122**/\r
149d6335
RN
123EFI_STATUS\r
124FatQueueTask (\r
bcdcc416
MK
125 IN FAT_IFILE *IFile,\r
126 IN FAT_TASK *Task\r
149d6335 127 )\r
149d6335 128{\r
bcdcc416
MK
129 EFI_STATUS Status;\r
130 LIST_ENTRY *Link;\r
131 LIST_ENTRY *NextLink;\r
132 FAT_SUBTASK *Subtask;\r
149d6335
RN
133\r
134 //\r
135 // Sometimes the Task doesn't contain any subtasks, signal the event directly.\r
136 //\r
137 if (IsListEmpty (&Task->Subtasks)) {\r
138 Task->FileIoToken->Status = EFI_SUCCESS;\r
139 gBS->SignalEvent (Task->FileIoToken->Event);\r
140 FreePool (Task);\r
141 return EFI_SUCCESS;\r
142 }\r
143\r
144 EfiAcquireLock (&FatTaskLock);\r
145 InsertTailList (&IFile->Tasks, &Task->Link);\r
146 EfiReleaseLock (&FatTaskLock);\r
147\r
148 Status = EFI_SUCCESS;\r
25510147
HW
149 //\r
150 // Use NextLink to store the next link of the list, because Link might be remove from the\r
151 // doubly-linked list and get freed in the end of current loop.\r
152 //\r
153 // Also, list operation APIs like IsNull() and GetNextNode() are avoided during the loop, since\r
154 // they may check the validity of doubly-linked lists by traversing them. These APIs cannot\r
155 // handle list elements being removed during the traverse.\r
156 //\r
157 for ( Link = GetFirstNode (&Task->Subtasks), NextLink = GetNextNode (&Task->Subtasks, Link)\r
bcdcc416
MK
158 ; Link != &Task->Subtasks\r
159 ; Link = NextLink, NextLink = Link->ForwardLink\r
160 )\r
161 {\r
149d6335
RN
162 Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
163 if (Subtask->Write) {\r
149d6335
RN
164 Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx (\r
165 IFile->OFile->Volume->DiskIo2,\r
166 IFile->OFile->Volume->MediaId,\r
167 Subtask->Offset,\r
168 &Subtask->DiskIo2Token,\r
169 Subtask->BufferSize,\r
170 Subtask->Buffer\r
171 );\r
172 } else {\r
173 Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx (\r
174 IFile->OFile->Volume->DiskIo2,\r
175 IFile->OFile->Volume->MediaId,\r
176 Subtask->Offset,\r
177 &Subtask->DiskIo2Token,\r
178 Subtask->BufferSize,\r
179 Subtask->Buffer\r
180 );\r
181 }\r
bcdcc416 182\r
149d6335
RN
183 if (EFI_ERROR (Status)) {\r
184 break;\r
185 }\r
186 }\r
187\r
188 if (EFI_ERROR (Status)) {\r
189 EfiAcquireLock (&FatTaskLock);\r
190 //\r
191 // Remove all the remaining subtasks when failure.\r
192 // We shouldn't remove all the tasks because the non-blocking requests have\r
193 // been submitted and cannot be canceled.\r
194 //\r
195 while (!IsNull (&Task->Subtasks, Link)) {\r
196 Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
bcdcc416 197 Link = FatDestroySubtask (Subtask);\r
149d6335
RN
198 }\r
199\r
200 if (IsListEmpty (&Task->Subtasks)) {\r
201 RemoveEntryList (&Task->Link);\r
202 FreePool (Task);\r
203 } else {\r
204 //\r
205 // If one or more subtasks have been already submitted, set FileIoToken\r
206 // to NULL so that the callback won't signal the event.\r
207 //\r
208 Task->FileIoToken = NULL;\r
209 }\r
210\r
211 EfiReleaseLock (&FatTaskLock);\r
212 }\r
213\r
214 return Status;\r
215}\r
216\r
cae7420b
DB
217/**\r
218\r
219 Set the volume as dirty or not.\r
220\r
221 @param Volume - FAT file system volume.\r
222 @param IoMode - The access mode.\r
223 @param DirtyValue - Set the volume as dirty or not.\r
224\r
44c9618a 225 @retval EFI_SUCCESS - Set the new FAT entry value successfully.\r
cae7420b
DB
226 @return other - An error occurred when operation the FAT entries.\r
227\r
228**/\r
b9ec9330
QH
229EFI_STATUS\r
230FatAccessVolumeDirty (\r
bcdcc416
MK
231 IN FAT_VOLUME *Volume,\r
232 IN IO_MODE IoMode,\r
233 IN VOID *DirtyValue\r
b9ec9330 234 )\r
b9ec9330 235{\r
bcdcc416 236 UINTN WriteCount;\r
b9ec9330
QH
237\r
238 WriteCount = Volume->FatEntrySize;\r
149d6335
RN
239 return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue, NULL);\r
240}\r
241\r
242/**\r
cae7420b 243 Invoke a notification event.\r
149d6335
RN
244\r
245 @param Event Event whose notification function is being invoked.\r
246 @param Context The pointer to the notification function's context,\r
247 which is implementation-dependent.\r
248\r
249**/\r
250VOID\r
251EFIAPI\r
252FatOnAccessComplete (\r
bcdcc416
MK
253 IN EFI_EVENT Event,\r
254 IN VOID *Context\r
149d6335 255 )\r
149d6335 256{\r
bcdcc416
MK
257 EFI_STATUS Status;\r
258 FAT_SUBTASK *Subtask;\r
259 FAT_TASK *Task;\r
149d6335
RN
260\r
261 //\r
262 // Avoid someone in future breaks the below assumption.\r
263 //\r
264 ASSERT (EfiGetCurrentTpl () == FatTaskLock.Tpl);\r
265\r
bcdcc416 266 Subtask = (FAT_SUBTASK *)Context;\r
149d6335
RN
267 Task = Subtask->Task;\r
268 Status = Subtask->DiskIo2Token.TransactionStatus;\r
269\r
270 ASSERT (Task->Signature == FAT_TASK_SIGNATURE);\r
271 ASSERT (Subtask->Signature == FAT_SUBTASK_SIGNATURE);\r
272\r
273 //\r
274 // Remove the task unconditionally\r
275 //\r
276 FatDestroySubtask (Subtask);\r
277\r
278 //\r
279 // Task->FileIoToken is NULL which means the task will be ignored (just recycle the subtask and task memory).\r
280 //\r
281 if (Task->FileIoToken != NULL) {\r
282 if (IsListEmpty (&Task->Subtasks) || EFI_ERROR (Status)) {\r
283 Task->FileIoToken->Status = Status;\r
284 gBS->SignalEvent (Task->FileIoToken->Event);\r
285 //\r
286 // Mark Task->FileIoToken to NULL so that the subtasks belonging to the task will be ignored.\r
287 //\r
288 Task->FileIoToken = NULL;\r
289 }\r
290 }\r
291\r
292 if (IsListEmpty (&Task->Subtasks)) {\r
293 RemoveEntryList (&Task->Link);\r
294 FreePool (Task);\r
295 }\r
b9ec9330
QH
296}\r
297\r
cae7420b
DB
298/**\r
299\r
300 General disk access function.\r
301\r
302 @param Volume - FAT file system volume.\r
303 @param IoMode - The access mode (disk read/write or cache access).\r
304 @param Offset - The starting byte offset to read from.\r
305 @param BufferSize - Size of Buffer.\r
306 @param Buffer - Buffer containing read data.\r
307 @param Task point to task instance.\r
308\r
309 @retval EFI_SUCCESS - The operation is performed successfully.\r
44c9618a 310 @retval EFI_VOLUME_CORRUPTED - The access is\r
cae7420b
DB
311 @return Others - The status of read/write the disk\r
312\r
313**/\r
b9ec9330
QH
314EFI_STATUS\r
315FatDiskIo (\r
bcdcc416
MK
316 IN FAT_VOLUME *Volume,\r
317 IN IO_MODE IoMode,\r
318 IN UINT64 Offset,\r
319 IN UINTN BufferSize,\r
320 IN OUT VOID *Buffer,\r
321 IN FAT_TASK *Task\r
b9ec9330 322 )\r
b9ec9330
QH
323{\r
324 EFI_STATUS Status;\r
325 EFI_DISK_IO_PROTOCOL *DiskIo;\r
326 EFI_DISK_READ IoFunction;\r
149d6335 327 FAT_SUBTASK *Subtask;\r
b9ec9330
QH
328\r
329 //\r
330 // Verify the IO is in devices range\r
331 //\r
332 Status = EFI_VOLUME_CORRUPTED;\r
333 if (Offset + BufferSize <= Volume->VolumeSize) {\r
334 if (CACHE_ENABLED (IoMode)) {\r
335 //\r
336 // Access cache\r
337 //\r
149d6335 338 Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer, Task);\r
b9ec9330
QH
339 } else {\r
340 //\r
341 // Access disk directly\r
342 //\r
149d6335
RN
343 if (Task == NULL) {\r
344 //\r
345 // Blocking access\r
346 //\r
bcdcc416
MK
347 DiskIo = Volume->DiskIo;\r
348 IoFunction = (IoMode == ReadDisk) ? DiskIo->ReadDisk : DiskIo->WriteDisk;\r
349 Status = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer);\r
149d6335
RN
350 } else {\r
351 //\r
352 // Non-blocking access\r
353 //\r
354 Subtask = AllocateZeroPool (sizeof (*Subtask));\r
355 if (Subtask == NULL) {\r
bcdcc416 356 Status = EFI_OUT_OF_RESOURCES;\r
149d6335
RN
357 } else {\r
358 Subtask->Signature = FAT_SUBTASK_SIGNATURE;\r
359 Subtask->Task = Task;\r
bcdcc416 360 Subtask->Write = (BOOLEAN)(IoMode == WriteDisk);\r
149d6335
RN
361 Subtask->Offset = Offset;\r
362 Subtask->Buffer = Buffer;\r
363 Subtask->BufferSize = BufferSize;\r
bcdcc416
MK
364 Status = gBS->CreateEvent (\r
365 EVT_NOTIFY_SIGNAL,\r
366 TPL_NOTIFY,\r
367 FatOnAccessComplete,\r
368 Subtask,\r
369 &Subtask->DiskIo2Token.Event\r
370 );\r
149d6335
RN
371 if (!EFI_ERROR (Status)) {\r
372 InsertTailList (&Task->Subtasks, &Subtask->Link);\r
373 } else {\r
374 FreePool (Subtask);\r
375 }\r
376 }\r
377 }\r
b9ec9330
QH
378 }\r
379 }\r
380\r
381 if (EFI_ERROR (Status)) {\r
382 Volume->DiskError = TRUE;\r
917e98f3 383 DEBUG ((DEBUG_ERROR, "FatDiskIo: error %r\n", Status));\r
b9ec9330
QH
384 }\r
385\r
386 return Status;\r
387}\r
388\r
cae7420b
DB
389/**\r
390\r
391 Lock the volume.\r
392\r
393**/\r
b9ec9330
QH
394VOID\r
395FatAcquireLock (\r
396 VOID\r
397 )\r
b9ec9330
QH
398{\r
399 EfiAcquireLock (&FatFsLock);\r
400}\r
401\r
cae7420b 402/**\r
b9ec9330 403\r
25ce9b1f
QH
404 Lock the volume.\r
405 If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned.\r
406 Otherwise, EFI_SUCCESS is returned.\r
b9ec9330 407\r
cae7420b
DB
408 @retval EFI_SUCCESS - The volume is locked.\r
409 @retval EFI_ACCESS_DENIED - The volume could not be locked because it is already locked.\r
b9ec9330 410\r
cae7420b
DB
411**/\r
412EFI_STATUS\r
413FatAcquireLockOrFail (\r
414 VOID\r
415 )\r
b9ec9330 416{\r
25ce9b1f 417 return EfiAcquireLockOrFail (&FatFsLock);\r
b9ec9330
QH
418}\r
419\r
cae7420b
DB
420/**\r
421\r
422 Unlock the volume.\r
423\r
424**/\r
b9ec9330
QH
425VOID\r
426FatReleaseLock (\r
427 VOID\r
428 )\r
b9ec9330
QH
429{\r
430 EfiReleaseLock (&FatFsLock);\r
431}\r
432\r
cae7420b 433/**\r
b9ec9330
QH
434\r
435 Free directory entry.\r
436\r
cae7420b 437 @param DirEnt - The directory entry to be freed.\r
b9ec9330 438\r
cae7420b
DB
439**/\r
440VOID\r
441FatFreeDirEnt (\r
bcdcc416 442 IN FAT_DIRENT *DirEnt\r
cae7420b 443 )\r
b9ec9330
QH
444{\r
445 if (DirEnt->FileString != NULL) {\r
446 FreePool (DirEnt->FileString);\r
447 }\r
448\r
449 FreePool (DirEnt);\r
450}\r
451\r
cae7420b 452/**\r
b9ec9330
QH
453\r
454 Free volume structure (including the contents of directory cache and disk cache).\r
455\r
cae7420b 456 @param Volume - The volume structure to be freed.\r
b9ec9330 457\r
cae7420b
DB
458**/\r
459VOID\r
460FatFreeVolume (\r
bcdcc416 461 IN FAT_VOLUME *Volume\r
cae7420b 462 )\r
b9ec9330
QH
463{\r
464 //\r
465 // Free disk cache\r
466 //\r
467 if (Volume->CacheBuffer != NULL) {\r
468 FreePool (Volume->CacheBuffer);\r
469 }\r
bcdcc416 470\r
b9ec9330
QH
471 //\r
472 // Free directory cache\r
473 //\r
474 FatCleanupODirCache (Volume);\r
475 FreePool (Volume);\r
476}\r
477\r
cae7420b
DB
478/**\r
479\r
480 Translate EFI time to FAT time.\r
481\r
482 @param ETime - The time of EFI_TIME.\r
483 @param FTime - The time of FAT_DATE_TIME.\r
484\r
485**/\r
b9ec9330
QH
486VOID\r
487FatEfiTimeToFatTime (\r
bcdcc416
MK
488 IN EFI_TIME *ETime,\r
489 OUT FAT_DATE_TIME *FTime\r
b9ec9330 490 )\r
b9ec9330
QH
491{\r
492 //\r
493 // ignores timezone info in source ETime\r
494 //\r
495 if (ETime->Year > 1980) {\r
bcdcc416 496 FTime->Date.Year = (UINT16)(ETime->Year - 1980);\r
b9ec9330
QH
497 }\r
498\r
499 if (ETime->Year >= 1980 + FAT_MAX_YEAR_FROM_1980) {\r
500 FTime->Date.Year = FAT_MAX_YEAR_FROM_1980;\r
501 }\r
502\r
bcdcc416
MK
503 FTime->Date.Month = ETime->Month;\r
504 FTime->Date.Day = ETime->Day;\r
505 FTime->Time.Hour = ETime->Hour;\r
506 FTime->Time.Minute = ETime->Minute;\r
507 FTime->Time.DoubleSecond = (UINT16)(ETime->Second / 2);\r
b9ec9330
QH
508}\r
509\r
cae7420b
DB
510/**\r
511\r
512 Translate Fat time to EFI time.\r
513\r
514 @param FTime - The time of FAT_DATE_TIME.\r
515 @param ETime - The time of EFI_TIME..\r
516\r
517**/\r
b9ec9330
QH
518VOID\r
519FatFatTimeToEfiTime (\r
bcdcc416
MK
520 IN FAT_DATE_TIME *FTime,\r
521 OUT EFI_TIME *ETime\r
b9ec9330 522 )\r
b9ec9330 523{\r
bcdcc416
MK
524 ETime->Year = (UINT16)(FTime->Date.Year + 1980);\r
525 ETime->Month = (UINT8)FTime->Date.Month;\r
526 ETime->Day = (UINT8)FTime->Date.Day;\r
527 ETime->Hour = (UINT8)FTime->Time.Hour;\r
528 ETime->Minute = (UINT8)FTime->Time.Minute;\r
529 ETime->Second = (UINT8)(FTime->Time.DoubleSecond * 2);\r
b9ec9330
QH
530 ETime->Nanosecond = 0;\r
531 ETime->TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
532 ETime->Daylight = 0;\r
533}\r
534\r
cae7420b 535/**\r
b9ec9330
QH
536\r
537 Get Current FAT time.\r
538\r
cae7420b 539 @param FatNow - Current FAT time.\r
b9ec9330 540\r
cae7420b
DB
541**/\r
542VOID\r
543FatGetCurrentFatTime (\r
bcdcc416 544 OUT FAT_DATE_TIME *FatNow\r
cae7420b 545 )\r
b9ec9330 546{\r
bcdcc416
MK
547 EFI_STATUS Status;\r
548 EFI_TIME Now;\r
ab99061c
OM
549\r
550 Status = gRT->GetTime (&Now, NULL);\r
551 if (!EFI_ERROR (Status)) {\r
552 FatEfiTimeToFatTime (&Now, FatNow);\r
553 } else {\r
554 ZeroMem (&Now, sizeof (EFI_TIME));\r
bcdcc416 555 Now.Year = 1980;\r
ab99061c 556 Now.Month = 1;\r
bcdcc416 557 Now.Day = 1;\r
ab99061c
OM
558 FatEfiTimeToFatTime (&Now, FatNow);\r
559 }\r
b9ec9330
QH
560}\r
561\r
cae7420b 562/**\r
b9ec9330
QH
563\r
564 Check whether a time is valid.\r
565\r
cae7420b 566 @param Time - The time of EFI_TIME.\r
b9ec9330 567\r
cae7420b
DB
568 @retval TRUE - The time is valid.\r
569 @retval FALSE - The time is not valid.\r
b9ec9330 570\r
cae7420b
DB
571**/\r
572BOOLEAN\r
573FatIsValidTime (\r
bcdcc416 574 IN EFI_TIME *Time\r
cae7420b 575 )\r
b9ec9330 576{\r
bcdcc416
MK
577 UINTN Day;\r
578 BOOLEAN ValidTime;\r
b9ec9330
QH
579\r
580 ValidTime = TRUE;\r
581\r
582 //\r
583 // Check the fields for range problems\r
584 // Fat can only support from 1980\r
585 //\r
bcdcc416
MK
586 if ((Time->Year < 1980) ||\r
587 (Time->Month < 1) ||\r
588 (Time->Month > 12) ||\r
589 (Time->Day < 1) ||\r
590 (Time->Day > 31) ||\r
591 (Time->Hour > 23) ||\r
592 (Time->Minute > 59) ||\r
593 (Time->Second > 59) ||\r
594 (Time->Nanosecond > 999999999)\r
595 )\r
596 {\r
b9ec9330 597 ValidTime = FALSE;\r
b9ec9330
QH
598 } else {\r
599 //\r
600 // Perform a more specific check of the day of the month\r
601 //\r
3c8b45c8 602 Day = mMonthDays[Time->Month - 1];\r
bcdcc416 603 if ((Time->Month == 2) && IS_LEAP_YEAR (Time->Year)) {\r
b9ec9330
QH
604 Day += 1;\r
605 //\r
606 // 1 extra day this month\r
607 //\r
608 }\r
bcdcc416 609\r
b9ec9330
QH
610 if (Time->Day > Day) {\r
611 ValidTime = FALSE;\r
612 }\r
613 }\r
614\r
615 return ValidTime;\r
616}\r