מעמקי הקלט/פלט ב-Windows (פרק 5) — המבנה הפנימי של NTFS: מערכת קבצים מתוך הבנת MFT

· · Windows, NTFS, I/O, מערכת קבצים, MFT, ליבה, .NET, חקירת תקלות

בארבעת הפרקים עד כאן עקבנו אחרי איך בקשת קלט/פלט זורמת (פרקים 1–3) ואיך מנהל המטמון מקבל אותה (פרק 4). בסוף הבקשה מגיעה למערכת הקבצים. הפעם תור הדוגמה המובילה שלה, NTFS.

נקודת המבט משתנה. עד עכשיו הסיפור היה דינמי — זרימת בקשה. הפעם זה סיפור סטטי על איך נתונים מסודרים בדיסק. הזהות האמיתית של «Zone.Identifier» הבלתי נראה שמצורף לקובץ שהורד. למה העתקת עשרת אלפים קבצים קטנים איטית בהרבה מהעתקת קובץ אחד באותו גודל כולל. עד כמה באמת נכון ש«NTFS עם יומן, אז זה בטוח» — הכול ניתן להסביר מהמבנה הזה.

זה פרק 5 בסדרה «מעמקי הקלט/פלט ב-Windows».

הנחה לפרק הזה: עוזר שכבר יש אחיזה ביסודות IRP ומחסנית ההתקנים מפרק 1. עם זאת, כדי שהמאמר יעמוד לבדו, הנה הגדרות למונחים מפרקים קודמים שמופיעים בגוף להלן.

מונח בשורה אחת פירוט
IRP (I/O Request Packet) «תלוש בקשת הקלט/פלט» שקריאת API כמו ReadFile מומרת אליו בתוך הליבה. מנהלים מקבלים את התלוש הזה ופועלים עליו פרק 1
מנהל הקלט/פלט ומחסנית ההתקנים רכיב הליבה שיוצר את ה-IRP ומעביר אותו, לפי הסדר, במחסנית המנהלים שערומים בדרך להתקן היעד — והמחסנית עצמה פרק 1
מנהל המטמון הרכיב ששומר את תוכן הקובץ בזיכרון ואחר כך שוטף לדיסק את התוכן שהצטבר מקריאות WriteFile. שורש «עצם זה שכתבתם אינו אומר שהגיע לדיסק» פרק 4

טבלה 1: מונחים מפרקים קודמים שמונחים בפרק הזה

עוד דבר, שני השלבים שטופלו בפרק 1 — cleanup (כשנסגר ה-handle האחרון) ו-close (כשכל ההפניות בתוך הליבה נעלמו) — ישמשו בהסבר מחיקת קבצים בפרק 4.

1. השורה התחתונה קודם

  • מרכז NTFS הוא MFT (Master File Table). כל הקבצים מנוהלים כרשומות בפנקס בתוך MFT, וכל המידע על קובץ נמצא «בתוך רשומת MFT» או «באזור מחוץ ל-MFT שהרשומה מצביעה עליו» (פרק 2).1
  • ישות הקובץ היא «אוסף מאפיינים». קובץ קטן נכנס כולו, כולל גוף הנתונים, לרשומת MFT (תושב), וקובץ גדול מחזיק רק הפניה לעמודת אשכולות (לא-תושב). האיטיות של עיבוד המוני של קבצים קטנים מוסברת מכאן (פרק 2).1
  • אפשר להחזיק כמה נתונים (כמה זרמי נתונים). הנתונים הרגילים הם «זרם חסר שם», ואפשר להחזיק זרם נוסף ב-שםקובץ:שם. זהות Zone.Identifier (Mark of the Web) (פרק 3).2
  • גם שם הוא מאפיין. כמה שמות לאותה רשומה הם קישור קשיח. גם שם מקוצר 8.3 מתגורר כ«עוד שם» (פרק 4).34
  • נקודת reparse היא המנגנון הרשמי של «כשפותחים, מקום אחר». קישור סימבולי, junction וקבצים לפי דרישה ב-OneDrive כולם יישומים של הנתונים המתויגים האלה (פרק 5).56
  • יש שני יומנים. $LogFile הוא לשחזור עקביות מטא-נתונים (יומן מקדים כדי לא להישבר), יומן USN הוא לרישום היסטוריית שינויים (פנקס של מה השתנה). התפקידים שונים לגמרי (פרק 6).78
  • «גודל» ו«גודל בדיסק» הם דברים שונים. קבצים דלילים ודחיסה יוצרים את הפער. גם הרקע ל«קובץ דחוס אינו הופך לאסינכרוני» שראינו בפרק 2 נמצא כאן (פרק 7).910

מפת הידע של המאמר

מרכז NTFS הוא ה-MFT (טבלת הקבצים הראשית), וכל הקבצים מנוהלים כרשומות בפנקס שבתוך ה-MFT. נתונים קטנים תושבים בתוך הרשומה, ונתונים גדולים הופכים ללא-תושבים ומחזיקים רק הפניה לרצף אשכולות (רצף נתונים) — וזהו מקור האיטיות בטיפול בהמון קבצים קטנים ושל הפיצול. כמה זרמי נתונים, קישור קשיח ושמות מקוצרים 8.3 הם כולם יישומים של אותו מנגנון «אוסף מאפיינים»; נקודת ניתוח מחדש היא ה-hook הרשמי על «פתיחה» שתומך מקישור סמלי ועד קבצים לפי דרישה של OneDrive. מה ש-$LogFile מגן עליו הוא עקביות המבנה; עמידות תוכן הנתונים צריך לבנות בנפרד בכלי בקרת המטמון של החלק הרביעי.

מפת הידע של המבנה הפנימי של NTFS ושל MFTתרשים שמראה את הקשרים בין NTFS, ‏MFT, רשומת קובץ, מאפיינים תושבים ולא-תושבים, רצפי נתונים, פיצול, כמה זרמי נתונים, ‏Zone.Identifier, קישור קשיח, שמות מקוצרים 8.3, נקודות ניתוח מחדש (קישור סמלי, צומת, קבצים לפי דרישה של OneDrive), ‏$LogFile ויומן USN, קבצים דלילים ודחיסת NTFSמשתמש במשתמש במחייבמצמצםמשתמש במשתמש באינו מתיישב עםמשתמש בעלול לגרום לנבדק באמצעותמשתמש בנשמר בנבדק באמצעותנבדק באמצעותעלול לגרום למשתמש במחייבמשתמש במוגדר באמצעותמשתמש בנבדק באמצעותמממש אתמממש אתמממש אתמשתמש במצמצםמשתמש במשתמש בנבדק באמצעותמשתמש בעלול לגרום למשתמש בעלול לגרום לאינו מתיישב עםנבדק באמצעותNTFSMFT (טבלת הקבצים הראשית)רשומת קובץ של MFTאזור MFTפיצול (NTFS)מאפיין תושב (resident)מאפיין לא-תושב (non-resident)רצף נתונים (data run)fsutilזרם נתונים חלופי (ADS)Zone.Identifier (Mark of the Web)streams (Sysinternals)פער בין גודל לוגי לשימוש בדיסקקישור קשיחשם מקוצר 8.3נקודת ניתוח מחדש (reparse point)קישור סמליצומת (נקודת עגינה)קבצים לפי דרישה של OneDrive$LogFile (יומן העסקאות של NTFS)פגיעה בכרךמנהל המטמוןיומן USN (יומן השינויים)קובץ דלילדחיסת NTFSקלט/פלט אסינכרוניProcess Monitor(procmon.exe)

בתרשים, קו מלא מציין קשר שמתקיים תמיד וקו מקווקו מציין קשר מותנה (תנאי ההתקיימות מפורטים בהסבר של כל קשר בעמוד המפורט). רשימת כל הקשרים (סך הכול 35, עם אסמכתה ורמת ודאות) והגדרות המושגים המרכזיים מרוכזות בעמוד המפורט של מפת הידע (ביפנית). נתונים: JSON-LD / Turtle

2. הכול הוא רשומת MFT

2.1. הפנקס של הכרך

כשמפרמטים כרך NTFS, נוצרים MFT (master file table) וסדרת קובצי מטא-נתונים שמתחילים ב-$. ב-MFT יש לפחות רשומה אחת לכל קובץ בכרך, וגם רשומה של MFT עצמה כלולה.1

כרך NTFSהרשומה מצביעה על המיקום$MFT ── טבלת הקבצים הראשיתפנקס רשומות כל הקבצים(גם היא עצמה רשומה)אזור נתוני משתמש(מקום הנחת נתונים לא-תושבים)$LogFile ── יומן עסקאות שלפעולות מטא-נתונים(פרק 6)$Bitmap ── מצב השימוש באשכולות$Boot / $Secure / $UpCase וכו'קובצי מטא-נתונים אחרים

איור 1: מבנה כרך NTFS. «גם מידע הניהול של מערכת הקבצים עצמה מוחזק כקובץ» הוא תכנון NTFS

המידע על קובץ — גודל, חותמות זמן, הרשאות גישה, ואפילו תוכן הנתוניםנשמר בתוך רשומת MFT, או באזור מחוץ ל-MFT שהרשומה מתארת את מיקומו.1 כשמוחקים קובץ הרשומה מסומנת כ«פנויה» וממוחזרת, אבל MFT עצמה אינה מצטמצמת. בנוסף, כדי לשמור על MFT רציפה שמור אזור שנקרא אזור MFT, וכשהכרך מתמלא מתחיל פיצול של MFT — גם סיפור אורך החיים הזה כתוב בתיעוד הרשמי.1

2.2. קובץ = אוסף מאפיינים, תושב ולא-תושב

תוכן רשומת הקובץ הוא רשימת מאפיינים. מידע תקני (חותמות זמן וכו’), שם קובץ, אבטחה, ואז נתונים. כאן יש פיצול חשוב.

רשומת קובץ MFT(פנקס של קובץ אחד)עד כמה מאות בתיםמעבר לזהמאפיין מידע תקניחותמות זמן · דגלי מאפייניםמאפיין שם קובץ(אפשר כמה ── פרק 4)מאפיין נתוניםהאם הנתונים קטניםתושב(resident)גוף הנתונים נכנס לרשומההקריאה מסתיימת בגישה ל-MFT בלבדלא-תושב(non-resident)ברשומה רק «הפניה לעמודת אשכולות»הנתונים האמיתיים באזור נתוני המשתמש

איור 2: רשומת קובץ היא אוסף מאפיינים. אם הנתונים קטנים הם «תושבים» בתוך הרשומה

קל יותר לראות איך תוכן אותה רשומה משתנה בין תושב ללא-תושב כשמניחים זה לצד זה.

לא-תושב(non-resident) ── קובץ גדולמצביע על המיקוםמצביע על המיקוםרשומת קובץ MFT(אורך קבוע)מידע תקני / שם קובץ / אבטחה─────────────מאפיין נתונים = טבלת ריצות נתוניםרצף 『מאיפה כמה אשכולות』אזור נתוני משתמשריצה 1: אשכולות רציפיםאזור נתוני משתמשריצה 2: אשכולות רציפים במקום אחרתושב(resident) ── קובץ קטןרשומת קובץ MFT(אורך קבוע)מידע תקני / שם קובץ / אבטחה─────────────מאפיין נתונים = התוכן עצמו『ערך הגדרה=1』 נכנס לכאן ישירותאין מקום הנחה נפרד בדיסקהקריאה מסתיימת בגישה ל-MFT בלבד

איור 3: השוואת תושב ולא-תושב. בלא-תושב, מה שיש לרשומה הוא רק הטבלה של «איפה וכמה נתונים אמיתיים» (ריצות נתונים)

ככל שמספר ריצות הנתונים גדל, קריאת קובץ אחד דורשת לעבור באזורים מפוזרים. זו זהות הפיצול שמופיעה בהמשך.

מהמבנה הזה אפשר להסביר כמה תופעות שפוגשים בשטח.

  • למה העתקת 10,000 קבצים קטנים איטית. לכל קובץ מתרחשות פעולות מטא-נתונים של יצירת רשומת MFT, רישום שם והגדרת אבטחה. עבודת הפנקס שולטת יותר מהעברת הנתונים עצמה (ואז כל אחת מהן הופכת גם ליעד לבדיקת המסננים שנראה בפרק 6).
  • זהות הפיצול. נתונים לא-תושבים נרשמים כ«עמודת קטעי אשכולות רציפים (ריצות)». אם אי אפשר לקבל אזור רציף, מספר הריצות גדל ומספר החיפושים הנדרשים לקריאה גדל — זה פיצול. את סידור הריצות בפועל אפשר להציץ ב-fsutil file layout.
  • גם «תיקייה» אינה מיוחדת. ספרייה היא «קובץ שמחזיק אינדקס משם קובץ למספר רשומת MFT». על הפנקס, הכול יושב על אותו מנגנון.

3. נתונים הם רק אחד מ«הזרמים»

3.1. קובץ אחד, כמה רצפי בתים

ב-NTFS קובץ אחד יכול להחזיק כמה זרמי נתונים. מה שקוראים וכותבים בדרך כלל ב-ReadFile/WriteFile הוא זרם ברירת המחדל חסר השם, ובמשפט שםקובץ:שםזרם אפשר ליצור זרם נתונים חלופי (ADS).2

הקובץ report.docx(רשומת MFT אחת)זרם ברירת מחדל(חסר שם)= התוכן שרואים בדרך כלל:Zone.Identifierמידע מקור(Mark of the Web):שם שרירותימידע נוסף ייחודי ליישום

איור 4: כמה זרמי נתונים. מה שמופיע בתצוגת הגודל בסייר הוא רק זרם ברירת המחדל

ה-ADS הקרוב ביותר הוא Zone.Identifier. לקובץ שהורד בדפדפן נרשם בזרם הזה המקור (שמקורו באינטרנט וכו’), והוא הופך לחומר שיפוט ל«Windows הגנה על המחשב» של SmartScreen ולתצוגה מוגנת של Office. הצד הגלוי של המנגנון הזה טופל ב«למה מופיע «Windows הגנה על המחשב»» — הזהות מאחורי הקלעים הייתה פשוט זרם NTFS.

3.2. המלכודות שמפתחים דורכים עליהן

  • בלתי נראה. אינו מופיע בגודל בסייר וגם לא ברשימת dir. אפשר לבדוק ב-dir /r או ב-streams של Sysinternals.11
  • אינו ניתן להעברה. ADS הוא תכונת NTFS, ולכן בהעתקה דרך כונן USB ב-FAT או אחסון ענן הוא נוטה להיאבד. «אזהרת ההורדה נעלמה אחרי שהעתקתי» זה זה.
  • אפשר לפתוח גם ביישום שלכם. די לכלול נקודתיים בנתיב כמו CreateFile("data.txt:meta", ...) כדי לקרוא ולכתוב.2 זה נוח, אבל מקבלים יחד את תכונת «אינו ניתן להעברה» מהסעיף הקודם, ולכן זה אינו מקום לגוף נתונים עסקיים.

4. גם שם הוא מאפיין — קישורים קשיחים ושמות 8.3

4.1. קישור קשיח — כמה שמות לאותה רשומה

באיור 2 כתבנו ש«אפשר להחזיק כמה מאפייני שם קובץ». בתוך אותו כרך, כמה נתיבים מפנים לקובץ יחיד — זה קישור קשיח (CreateHardLink / mklink /H).3

האינדקס של C:\\backup\\האינדקס של C:\\app\\config-link.json → רשומה #1234config.json → רשומה #1234רשומת MFT #1234גוף הנתונים(או הפניה לריצות)מספר קישורים: 2

איור 5: קישור קשיח. אינדקס הספרייה מצביע על אותה רשומת MFT, ושניהם «האמיתי»

שינוי מכל שם שהוא הוא אותו קובץ, ולכן התוכן תואם מיד.3 ואז משמעות «מחיקה» משתנה — DeleteFile הוא «להסיר שם אחד», והישות נעלמת רק כשהשם האחרון הוסר, ה-handle הפתוח נסגר, וגם כל ההפניות בתוך הליבה כמו מקטע מיפוי זיכרון נעלמו. שני השלבים שראינו בפרק 1, cleanup (ה-handle האחרון) ו-close (ההפניה האחרונה), פועלים כמו שהם גם על אורך החיים של המחיקה. שימו לב שיש מוזרות בתצוגת מאפיינים: גם אם משנים מאפיין דרך קישור אחד, התצוגה הנראית של קישור אחר עשויה להישאר ישנה — התנהגות שמצוינת רשמית.3

4.2. שם 8.3 — עוד שם נסתר

לתאימות היסטורית, NTFS יכולה ליצור אוטומטית שם מקוצר בפורמט 8.3 כמו REPORT~1.DOC לשמות קבצים ארוכים. גם זה מתגורר ברשומה כ«עוד שם». בתיקיות עם הרבה קבצים, יצירת השם המקוצר והימנעות מהתנגשות הופכות לעלות, ולכן אפשר להשבית יצירה או להסיר שמות מקוצרים קיימים ב-fsutil 8dot3name (אם יש יישום ישן שרושם נתיבי רישום בשם מקוצר זה נשבר, ולכן יש גם פונקציית בדיקה לפני strip — נקודה מעשית).4

כאן כדאי לשים לב שהאם יש שם מקוצר תלוי בסביבה. התנהגות ברירת המחדל נקבעת בערך הרישום NtfsDisable8dot3NameCreation, ויש ארבע אפשרויות: 0 (ליצור בכל הכרכים), 1 (לא ליצור באף כרך), 2 (להגדיר לפי כרך), 3 (לא ליצור מחוץ לכרך המערכת).4 אם בוחרים 2 אפשר להחליף לפי כרך, כך ש«ב-Windows תמיד קיים שם מקוצר כמו PROGRA~1» אינו בהכרח נכון. לפני שכותבים קוד או נוהל שתלוי בשם מקוצר, בדקו את המצב הנוכחי ב-fsutil 8dot3name query C: (השמטת הכרך מחזירה את ברירת המחדל המשותפת לכל הכרכים).

מלכודות סביב נתיבים ושמות (MAX_PATH, שמות שמורים, נקודה בסוף) מטופלות בפירוט ב«MAX_PATH והמלכודות של נתיבים ושמות קבצים ב-Windows». אם מחברים את פתרון השמות מפרק 1 (מנהל האובייקטים) לפרק הזה (שמות בתוך מערכת הקבצים), מתקבלת התמונה המלאה של «שם» ב-Windows.

5. נקודת reparse — המנגנון של «כשפותחים, מקום אחר»

אפשר לצרף לקובץ או לספרייה נקודת reparse. הישות היא מאפיין של «תג + נתונים שהמשתמש מגדיר». כשמערכת הקבצים פותחת קובץ עם נקודת reparse, העיבוד נחטף לפי התג — מנהל מסנן שמבין את התג לוקח את העיבוד, או בתגי שינוי שם הפתרון מתחיל מחדש בנתיב היעד.5

NTFSמנהל הקלט/פלטיישוםNTFSמנהל הקלט/פלטיישוםנמצאה נקודת reparse ביעדמחזירים תג ונתוניםמסנן שמבין את התגלוקח את העיבוד(פרק 6)alt[קישור סימבולי / junction(שינוי שם)][תג בניהול מסנן(קובץ ענן וכו')]CreateFile("C:\\data\\link.txt")IRP_MJ_CREATE(עולם פרק 1)「המקום האמיתי הוא כאן」פותרים מחדש בנתיב היעד

איור 6: פתרון נקודת reparse. זה הופך לוו רשמי שמתערב בפעולת «פתיחה»

על המנגנון האחד הזה עומדות תכונות מוכרות.

  • קישור סימבולי (mklink) — שלט שמחזיק נתיב יעד. יכול להצביע גם לכרך אחר או לנתיב UNC.6
  • Junction / נקודת mount — מנגנון ותיק שמחבר ספרייה למיקום בכרך מקומי אחר.3
  • קבצים לפי דרישה ב-OneDrive — מייצגים קובץ שאין לו נתונים אמיתיים ביד כנקודת reparse, וברגע הפתיחה המסנן מוריד ומגיש את התוכן. זו זהות «רואים בסייר, אבל כשפותחים רצה תקשורת» (מנגנון המסנן עצמו בפרק 6).

אזהרה מעשית אחת. «המקום שאליו הנתיב מצביע אינו בהכרח המקום המקומי הזה». כלי שעובר עץ באופן רקורסיבי נכנס ללולאה ב-junction, סיכום גדלים נכפל, גיבוי גורם להמון הגשמה של ענן — קוד שלא יודע על נקודות reparse דורך על אלה. אישור המאפיין FILE_ATTRIBUTE_REPARSE_POINT במשפחת FindFirstFile הוא פתח האמצעי.5

6. שני יומנים — $LogFile ו-USN

אומרים לעיתים קרובות «NTFS היא מערכת קבצים עם יומן», אבל ב-NTFS יש שני יומנים עם תפקידים שונים. בלבול ביניהם גורם לקרוא לא נכון את ההבטחה.

יומן USN ── היסטוריית שינויים(כדי לדעת מה השתנה)בכל שינוי לקובץ / ספרייהרושמים את תוכן השינוי ואת השםגיבוי · אינדקס חיפוש · כלי סנכרוןתופסים 「מה השתנה מהפעם הקודמת」 בלי סריקה מלאה$LogFile ── יומן מקדים(כדי לא להישבר)רושמים פעולת מטא-נתונים(עדכון רשומה · שינוי שם וכו')ליומן לפני הביצועבאתחול הבא אחרי כשל מערכתמשחזרים את היומן ומשחזרים עקביות מבנה

איור 7: שני היומנים. $LogFile הוא «כדי לא להישבר», USN הוא «כדי לדעת מה השתנה»

ההבדל בטבלה הוא כדלקמן.

היבט $LogFile (יומן עסקאות) יומן USN (יומן שינויים)
מטרה להחזיר את מבנה מערכת הקבצים למצב עקבי אחרי כשל7 לדעת אחר כך «מה השתנה מהפעם הקודמת»8
מה נרשם יומן מקדים של פעולות מטא-נתונים (עדכון רשומה, שינוי שם וכו’). תוכן הקובץ אינו יעד בכל שינוי, תוכן השינוי ושם הקובץ/ספרייה היעד8
מי משתמש NTFS עצמה. לשימוש בשחזור אוטומטי ב-mount הבא יישומים כמו גיבוי, אינדקס חיפוש וכלי סנכרון
עד כמה אפשר לחזור אחורה רק הטווח הנדרש לשחזור. גודל קבוע שממוחזר, ולכן אינו מתאים למעקב היסטוריה כשחורגים מגודל היעד (MaximumSize) רשומות ישנות נגזרות בנקודת ביקורת. הטווח שניתן לחזור אליו תלוי בהגדרת הגודל ובכמות העדכונים בכרך12
איך מעיינים אין אמצעי רשמי לקרוא את התוכן (אפשר לבדוק גודל ב-chkdsk /L) מצב ב-fsutil usn queryjournal, תוכן ב-fsutil usn readjournal. מתוכנית FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL12
אפשר לעצור? אי אפשר (חלק מ-NTFS) מנהל יכול למחוק ולהשבית. אבל זה כופה סריקה מלאה על שירותים בשימוש, וההשפעה גדולה12

טבלה 2: השוואת שני היומנים

  • $LogFile (יומן עסקאות) הוא יומן מקדים של פעולות מטא-נתונים. גם אם מתרחש כשל מערכת, NTFS משחזרת אוטומטית את עקביות מערכת הקבצים מהיומן הזה וממידע נקודת הביקורת באתחול הבא.7 מה שמוגן כאן הוא המבנה. כפי שראינו בפרק 4, תוכן נתונים מלוכלך במטמון יכול להיאבד בהפסקת חשמל — «הכרך לא יישבר. אבל הכתיבה האחרונה יכולה להיעלם» היא הקריאה הנכונה.
  • יומן USN (יומן שינויים) הוא פנקס שרושם בכל שינוי לקובץ או לספרייה בכרך את תוכן השינוי ושם היעד.8 זה מנגנון לגיבוי ולמאבדקים לאסוף «רק מה שהשתנה מהפעם הקודמת» בלי סריקה מלאה, ומשמש גם להימנעות מבנייה מחדש של אינדקס אחרי כשל.8 בפועל, כדאי לזכור שאפשר להשתמש ב-fsutil usn readjournal כפנקס הצלבה שמשלים פספוסים של FileSystemWatcherמדריך מעשי ל-FileSystemWatcher») בחקירה.

7. דליל ודחיסה — הסיפור שבו יש שני «גדלים»

ב-NTFS, האורך הלוגי של קובץ והאזור שהוקצה בפועל מנוהלים בנפרד. «גודל» ו«גודל בדיסק» במסך המאפיינים. שני נציגים יוצרים את הפער.

קובץ דליל אינו מקצה אזור אמיתי לטווחים שנמשכים כאפסים ומנהל אותם כ«חורים».9 קובץ דיסק וירטואלי בגודל לוגי 42GB שמשתמש רק ב-500MB בדיסק — קורה כדבר שבשגרה. קריאת חור מחזירה אפסים, וכתיבה מקצה רק את החלק הזה.

הקצאה בדיסק(15MB+מידע ניהול)קובץ לוגי(גודל: 1GB)ריצה: ישות R1ריצה: ישות R2נתונים 10MBחור(אפס) 500MBנתונים 5MBחור(אפס) היתרה

איור 8: קובץ דליל. ל«חורים» אין הקצאה, והגודל הלוגי והגודל בדיסק מתרחקים

דחיסת NTFS דוחסת ומאחסנת נתונים לפי יחידת דחיסה.10 זה שקוף ונוח, אבל גם העלות אינה שקופה — בכל קריאה וכתיבה רצים פריסה ודחיסה מחדש, וגם פיצול מתקדם בקלות. וכפי שראינו בפרק 2 פרק 5, גישה לקובץ דחוס אינה הופכת לאסינכרונית (מערכת הקבצים ממירה לסינכרוני). זה אחד המקומות לחשוד כש«יש קובץ שהפכתי לקלט/פלט אסינכרוני והוא לא מאיץ».

את הגודל האמיתי מבוסס ההקצאה אפשר לקבל ב-GetCompressedFileSize. בחקירת «סכום גדלי הקבצים» ו«שימוש בדיסק» לא תואמים, הנוהל הקבוע הוא לחשוד לפי הסדר בארבעה: דליל, דחיסה, ADS (פרק 3), ועיגול לאשכול.

8. לאשר במו עיניכם

גם הפעם אפשר לצפות בהכול ב-Windows שבידכם (חלק דורש הרשאות מנהל). כדי שתוכלו לשפוט בעצמכם אם תוצאת ההרצה נכונה, נצרף לכל פקודה איפה להסתכל ומה זה אומר.

:: לראות זרמי נתונים חלופיים
dir /r C:\Users\%USERNAME%\Downloads

כאן מסתכלים: מתחת לשורת הקובץ הרגילה מופיעות בשורה עם הזחה שורות בצורה שםקובץ:Zone.Identifier:$DATA עם אורך. אם יש שורה כזו, לקובץ הזה מצורף Mark of the Web (פרק 3). לקובץ שהורד בדפדפן זה מצורף, לקובץ שיצרתם בעצמכם לא. הרצה בשני המקומות והשוואה מבהירה את קיום ADS.

:: לראות את פריסת הקובץ (ריצות) ואת המאפיינים ב-MFT
fsutil file layout C:\path\to\file.dat

:: לראות extents בלבד (תת-פקודה המתועדת רשמית)
fsutil file queryextents C:\path\to\file.dat

כאן מסתכלים: layout מציג לכל זרם גודל, גודל מוקצה, ואם לא-תושב — רשימת extents (זוגות VCN, LCN ומספר אשכולות). קובץ קטן מאוד בלי שורות extent הוא תושב (סעיף 2.2); אם מתפצל לכמה שורות, הוא מפוצל. הדרך הקצרה ביותר לחוש תושב/לא-תושב היא להריץ על קובץ טקסט של כמה בתים ועל קובץ של מאות MB ולהשוות.

:: הגדרת יצירת שם מקוצר 8.3 ושמות מקוצרים קיימים
fsutil 8dot3name query C:
dir /x

כאן מסתכלים: query מחזיר אם יצירת שם מקוצר פעילה או מושבתת בכרך הזה (השמטת הכרך מחזירה את ברירת המחדל המשותפת לכל הכרכים).4 dir /x מציג עמודת שם מקוצר ליד השם הארוך, כך שעמודה ריקה פירושה שלא נוצר שם מקוצר. אפשר לאשר בסביבה שלכם את «אין בהכרח שם מקוצר» מסעיף 4.2.

:: מצב יומן USN
fsutil usn queryjournal C:

כאן מסתכלים: מוצגים מזהה היומן, טווח USN התקף (First USN / Next USN), גודל יעד (MaximumSize) ויחידת הקצאה (AllocationDelta).12 אם יוצרים קובץ כלשהו ומריצים שוב, Next USN אמור להתקדם, וזה אישור ש«השינוי נרשם». MaximumSize הוא מדד ל«עד כמה אפשר לחזור אחורה» שנגענו בו בפרק 6. בכרך שבו היומן מושבת מתקבלת שגיאה.

:: אישור נקודות reparse (יעד ותג)
dir /aL C:\Users\%USERNAME%
fsutil reparsepoint query "C:\Users\%USERNAME%\OneDrive"

כאן מסתכלים: מה שמופיע ב-dir /aL הם נקודות reparse (אלה ש-FILE_ATTRIBUTE_REPARSE_POINT דולק בהן). קישור סימבולי או junction מוצגים עם סוג כמו <SYMLINKD> <JUNCTION>. fsutil reparsepoint query מציג את ערך תג ה-reparse, ובסוג שינוי שם גם את נתיב היעד. ציון יעד שאינו נקודת reparse מחזיר שגיאה, כך שעצם השגיאה היא אישור ש«כאן זו תיקייה רגילה».

כשעוקבים אחרי פעולות קבצים ב-Procmon, הדמויות של הפרק הזה זורמות בשמן האמיתי (כתיבה ל-$LogFile, נתיבים עם שם זרם, עיבוד reparse). לשימוש ראו «מדריך מעשי ל-Process Monitor (ProcMon)».

9. סיכום

  • מרכז NTFS הוא MFT. כל הקבצים הם רשומות בפנקס, והמידע נמצא «בתוך הרשומה» או «באזור חיצוני שהרשומה מצביעה עליו». נתונים קטנים הם תושבים, נתונים גדולים הם הפניית ריצה, והאיטיות של עיבוד המוני של קבצים קטנים והפיצול הם מסקנות מהמבנה הזה.1
  • אפשר להחזיק כמה זרמי נתונים. Zone.Identifier (Mark of the Web) הוא פשוט ADS, נראה ב-dir /r, ואינו מועבר מחוץ ל-NTFS.211
  • שם הוא מאפיין, ואפשר כמה. קישור קשיח הוא שם באותו מעמד לאותה רשומה, ושם 8.3 הוא עוד שם לתאימות. «מחיקה = הסרת שם», והישות נעלמת כשכל השמות, ה-handles וההפניות בתוך הליבה (מקטעים ממורכבים וכו’) נעלמו.34
  • נקודת reparse היא וו רשמי ל«פתיחה», וגם קישור סימבולי, junction וקבצים לפי דרישה הם יישום של זה. קוד שעובר עץ צריך להיות מודע ל-FILE_ATTRIBUTE_REPARSE_POINT.56
  • יש שני יומנים. $LogFile הוא שחזור עקביות מבנה (לא להישבר), USN הוא היסטוריית שינויים (מה השתנה). «יש יומן ולכן גם הנתונים בטוחים» אינו נכון — עמידות הנתונים בונים בכלים מפרק 4.78
  • גודל לוגי והקצאה הם דברים שונים. דליל, דחיסה, ADS ועיגול לאשכול הם ארבעת הגורמים העיקריים ל«הגודל לא תואם». כולל העניין שקובץ דחוס אינו הופך לקלט/פלט אסינכרוני, זה מגירה לחקירת ביצועים.910

ההמשך הוא הפרק האחרון, פרק 6 «מנהלי מסנן ומיניפילטר — למה Procmon וסריקת אנטי-וירוס יכולים להתערב בקלט/פלט». אלה ש«יושבים באמצע» שהופיעו מפרק 1 מדי פעם — אנטי-וירוס, Procmon, OneDrive, הצפנה — איך הם מתערבים בקלט/פלט. כסיום הסדרה, נחשוף את זהות הדיירים שעומדים ברווח של מחסנית ההתקנים.

מאמרים קשורים

תחומי ייעוץ קשורים

KomuraSoft LLC מטפלת בתכנון וחקירה של יישומים עסקיים ל-Windows ששורשיהם במנגנון NTFS, כמו התנהגות בלתי מובנת של גודל קובץ או ביצועי העתקה, ותקלות סביב קישורים וזרמים.

מקורות

  1. Microsoft Learn, Master File Table. על כך שלכל קובץ בכרך NTFS יש לפחות רשומה אחת ב-MFT, וגם רשומת MFT עצמה כלולה; על כך שכל המידע כולל גודל קובץ, חותמות זמן, הרשאות גישה ותוכן נתונים נשמר בתוך רשומת MFT או באזור מחוץ ל-MFT שהרשומה מתארת את מיקומו; על כך שבמחיקת קובץ הרשומה מסומנת כפנויה וממוחזרת אבל גודל MFT אינו מצטמצם; על כך ששמור אזור MFT כדי לשמור על MFT רציפה; ועל כך שעם התקדמות ההקצאה מתרחש פיצול של MFT.  2 3 4 5 6

  2. Microsoft Learn, File Streams. על כך שנתוני קובץ NTFS מאוחסנים כזרם אחד או יותר; על כך שיש זרם נתונים ברירת מחדל (חסר שם) וזרמי נתונים חלופיים בעלי שם; ועל כך שאפשר לציין זרם בצורת «שםקובץ:שםזרם» ולפתוח ב-CreateFile.  2 3 4

  3. Microsoft Learn, fsutil 8dot3name. על כך ש-NTFS יכולה ליצור שם מקוצר בפורמט 8.3 לשמות קבצים ארוכים; ועל כך שב-fsutil 8dot3name אפשר לשאול ולהגדיר הפעלה/השבתה של יצירת שם מקוצר, להסיר שמות מקוצרים קיימים (strip), ולסרוק הפניות רישום שיושפעו אם מסירים.  2 3 4 5

  4. Microsoft Learn, Reparse points. על כך שנקודת reparse היא אוסף של נתונים שהמשתמש מגדיר ותג reparse שמזהה באופן ייחודי את פורמט הנתונים; על כך שבפתיחת קובץ עם נקודת reparse מערכת הקבצים מנסה עיבוד שתואם לתג (עיבוד בידי מסנן מערכת קבצים שמפרש את התג); על כך שהיא משמשת למימוש קישורי מערכת קבצים של NTFS ולאחסון מרוחק (זיכרון היררכי); ועל כך שאפשר לאשר קיום במאפיין FILE_ATTRIBUTE_REPARSE_POINT.  2 3 4

  5. Microsoft Learn, NTFS overview. על כך ש-NTFS משתמשת בקובץ יומן ובמידע נקודת ביקורת, ובמקרה של כשל מערכת משחזרת את יומן העסקאות באתחול הבא ומשחזרת אוטומטית את עקביות מערכת הקבצים; ועל כך שיש מיפוי דינמי מחדש של סקטורים פגומים, ו-self-healing NTFS שמתקן נזק קל ברקע.  2 3 4

  6. Microsoft Learn, Change Journals. על כך שבכל שינוי לקובץ או לספרייה בכרך נרשמים תוכן השינוי ושם הקובץ/ספרייה היעד ביומן השינויים USN של הכרך; על כך שהיומן נשמר לפי כרך; ועל כך שאפשר להשתמש בו לשחזור אינדקס מערכת קבצים אחרי כשל ולהימנע מאינדוקס מחדש של הכרך כולו.  2 3 4 5 6

  7. Microsoft Learn, Sparse Files. על כך שבקובץ דליל לא מקצים שטח דיסק פיזי לטווחים גדולים שמורכבים מאפסים, ומקצים אזור רק לחלקים שמכילים נתונים; ועל כך שקריאת טווח בלי הקצאה מחזירה אפסים.  2 3

  8. Microsoft Learn, File Compression and Decompression. על כך שדחיסת קבצים של NTFS נעשית בשקיפות, והנתונים נדחסים ומאוחסנים לפי יחידת דחיסה; על כך שב-GetCompressedFileSize אפשר לקבל את הגודל אחרי דחיסה (הקצאה אמיתית); ועל כך שקריאה וכתיבה של קובץ דחוס מלוות בעלות פריסה ודחיסה מחדש.  2 3

  9. Microsoft Learn, Streams - Sysinternals. על כך שכלי העזר streams של Sysinternals יכול למנות ולמחוק זרמי נתונים חלופיים של קובץ NTFS.  2

  10. Microsoft Learn, Creating, Modifying, and Deleting a Change Journal ו-fsutil usn. על כך ש-MaximumSize של יומן השינויים הוא ערך יעד, וכשהגודל חורג מסכום MaximumSize ו-AllocationDelta הוא נגזר בנקודת הביקורת של NTFS; על כך ש-AllocationDelta הוא יחידת ההוספה לסוף היומן והמחיקה מההתחלה; על כך שב-fsutil usn queryjournal אפשר לעיין במצב ובקיבולת היומן, וב-readjournal בתוכן הרשום; על כך שמתוכנית משתמשים ב-FSCTL_CREATE_USN_JOURNAL / FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL / FSCTL_DELETE_USN_JOURNAL; ועל כך שמחיקה או השבתה של יומן פעיל מלווה בסריקת MFT כולה וכופה על שירותים שמשתמשים ביומן סריקה מחדש של הכרך.  2 3 4

מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.

העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.

המאמר קשור ישירות לשירותים הבאים.

שאלות נפוצות

שאלות נפוצות בפניות בנושא המאמר.

מהי MFT (Master File Table)?
זו מבנה הנתונים בלב כרך NTFS — פנקס שמחזיק לפחות רשומה אחת (רשומת קובץ) לכל קובץ בכרך, כולל רשומה ל-MFT עצמה. כל מה שקשור לקובץ, מגודל, חותמות זמן והרשאות גישה ועד תוכן הנתונים עצמו, נשמר או בתוך רשומת MFT או באזור מחוץ ל-MFT שהרשומה מצביעה עליו. קובץ קטן נכנס כולו, כולל הנתונים, לתוך רשומת ה-MFT שלו (תושב, resident); לקובץ גדול נרשמת ברשומה רק הפניה לפריסת הנתונים (ריצה של אשכולות) (לא-תושב, non-resident). מחיקת קובץ מסמנת את הרשומה כפנויה לשימוש חוזר, אבל גודל ה-MFT עצמו אינו מצטמצם.
מהם נתוני «Zone.Identifier» הבלתי נראים שמצורפים לקבצים?
זה אחד מזרמי הנתונים המרובים של NTFS (זרמי נתונים חלופיים). ב-NTFS קובץ אחד יכול להחזיק כמה רצפי בתים (זרמים); מה שקוראים וכותבים בדרך כלל הוא הזרם ברירת המחדל חסר השם. Windows רושם מאיפה הקובץ הגיע — למשל שהורד מהאינטרנט — בזרם נוסף שמציינים בנקודתיים, כמו «file.txt:Zone.Identifier». זה מה שנקרא «Mark of the Web», וזה מה ש-SmartScreen ותצוגה מוגנת של Office משתמשים בו כראיה בשיפוטיהם. זרמים חלופיים אינם מופיעים בתצוגת הגודל בסייר; אפשר לבדוק אותם בפקודה dir /r או בכלי streams של Sysinternals. כדאי גם לשים לב שהם אינם נשמרים כשמעתיקים למערכת קבצים שאינה NTFS כמו FAT.
מה ההבדל בין קישור קשיח לקישור סימבולי?
קישור קשיח הוא «עוד שם באותו מעמד שנוסף, מצביע על אותה ישות קובץ — אותה רשומת MFT». אפשר ליצור אותו רק בתוך אותו כרך, גישה לקובץ דרך כל אחד משמותיו נותנת את אותו קובץ, ומחיקת שם אחד אינה מסירה את הקובץ כל עוד שם אחר נשאר. קישור סימבולי הוא «שלט שמפנה לנתיב אחר», ממומש כנקודת reparse. מכיוון שהוא מחזיק רק את נתיב היעד כמחרוזת, הוא יכול להצביע לכרך אחר או אפילו למיקום מרוחק, אבל הופך למבוי סתום אם היעד נעלם. בפועל, כלל האצבע: קישור קשיח לשיתוף הישות הבסיסית (שמשנה את משמעות המחיקה), וקישור סימבולי להפניית נתיב (להעברה או הפניה).
NTFS היא מערכת קבצים עם יומן, אז הנתונים שורדים הפסקת חשמל?
צריך להבין בדיוק מה מוגן. מה שיומן העסקאות של NTFS ($LogFile) מגן עליו הוא עקביות מבנה מערכת הקבצים (המטא-נתונים). גם אם מתרחש כשל מערכת, NTFS משחזרת עקביות אוטומטית באמצעות היומן באתחול הבא, ומונעת מצב שבו הכרך פגום ואינו ניתן לקריאה. אבל זה אינו אומר שתוכן הקובץ שהיה באמצע כתיבה משוחזר. כפי שראינו בפרק 4 של הסדרה, נתונים מלוכלכים שיושבים במטמון אובדים בהפסקת חשמל. לכן ההבנה הנכונה היא «הכרך לא יישבר, אבל תוכן הכתיבה האחרונה עדיין יכול להיעלם» — אם נדרשת עמידות לנתונים עצמם, צריך לבנות אותה בעצמכם, עם FlushFileBuffers, WRITE_THROUGH, או תכנון כתיבה ברמת היישום כמו כתיבה לקובץ זמני ואז שינוי שם.
למה «הגודל» של קובץ שונה מ«הגודל בדיסק»?
כי NTFS מנהלת בנפרד את האורך הלוגי של הקובץ ואת שטח הדיסק שמוקצה לו בפועל. גם בקובץ רגיל יש הבדל כלשהו כי ההקצאה מעוגלת כלפי מעלה לאשכולות שלמים (4KB כברירת מחדל), אבל קבצים דלילים וקבצים דחוסים הם המקום שבו הפער הופך גדול. קובץ דליל אינו מקצה אחסון אמיתי לטווחים שנמשכים כאפסים — הוא מנהל אותם כ«חורים» — כך שבהחלט אפשרי שקובץ בגודל לוגי של כמה ג'יגה-בתים ישתמש רק בכמה מגה-בתים בדיסק. לקובץ דחוס מוקצה רק הגודל אחרי דחיסה. להפך, אם «הגודל בדיסק» נראה גדול יותר, עיגול לאשכול או זרם נתונים חלופי הם לעיתים קרובות הסיבה.

פרופיל הכותב

עמוד היכרות עם כותב המאמר.

Go Komura

מנהל KomuraSoft LLC

מתמחה בפיתוח תוכנה עבור Windows, ייעוץ טכני וחקירת תקלות, בעיקר בפרויקטים עם מערכות קיימות ובאגים שקשה לשחזר.

קישורים ציבוריים

חזרה לבלוג