среда, 13 мая 2009 г.

Восстановление удаленных файлов под BSD: структура UFS

Внешне UFS очень похожа на ext2fs – те же inod'ы, блоки данных, файлы, директории… Но есть и отличия. В ext2fs имеется только одна группа inod'ов и только одна группа блоков данных для всего раздела. UFS же делит раздел на несколько зон одинакового размера, называемых группами цилиндров. Каждая зона имеет свою группу inod'ов и свою группу блоков данных, независимую ото всех остальных зон. Другим словами, inod'е описывают блоки данных той и только той зоны, к которой они принадлежат. Это увеличивает быстродействие файловой системы (головка жесткого диска совершает более короткие перемещения) и упрощает процедуру восстановления при значительном разрушении данных, поскольку, как показывает практика, обычно гибнет только первая группа inod'e. Чтобы погибли все группы… ну я даже не знаю что же такого с жестким диском нужно сделать. А! Знаю! Под пресс положить!

В UFS каждый блок разбит на несколько фрагментов фиксированного размера, предотвращающих потерю свободного пространства в хвостах файлов. Благодаря этому, использование блоков большого размера уже не кажется расточительной идей, напротив, это увеличивает производительность и уменьшает фрагментацию. Если файл использует более одного фрагмента в двух несмежных блоках, он автоматически перемещается на новое место, в наименее фрагментированный регион свободного пространства. Поэтому, фрагментация в UFS очень мала или же совсем отсутствует, что существенно облегчает восстановление удаленных файлов и разрушенных данных.

Адресация ведется либо по физическим смещениям, измеряемых в байтах и отсчитываемых от начала группы цилиндров (реже — UFS-раздела), либо в номерах фрагментов, отсчитываемых от тех же самых точек. Допустим, размер блока составляет 16 Кбайт, разбитых на 8 фрагментов. Тогда 69'й сектор будет иметь смещение 512 х 69 == 35328 байт или 1024 x (16/8)/512 x 69 = 276 фрагментов.


В начале раздела расположен загрузочный сектор, затем следует суперблок, за которым находится одна или несколько групп цилиндров. Для перестраховки, копия суперблока дублируется в каждой группе. Загрузочный сектор не дублируется, но по соображениям унификации и единообразия, под него просто выделяется место. Таким образом, относительная адресация блоков в каждой группе остается неизменной.

В UFS cуперблок располагается по смещению 8192 байт от начала раздела, что соответствует 16-сектору. В UFS2 он "переехал" на 65536 байт (128 секторов) от начала, освобождая место для дисковой метки и первичного загрузчика операционной системы, а для действительно больших (в исходных текстах — piggy, т. е. "свинских") систем предусмотрена возможность перемещения суперблока по адресу 262144 байт (целых 512 секторов)!


Среди прочей информации суперблок содержит:

 

q       cblkno — смещение первой группы блока цилиндров, измеряемый в фрагментах, отсчитываемых от начала раздела;

q       fs_iblkno — смещение первой inode в первой группе цилиндров (фрагменты от начала раздела);

q       fs_dblkno — смещение первого блока данных в первой группе цилиндров (фрагменты от начала раздела);

q       fs_ncg — кол-во групп цилиндров (штуки);

q       fs_bsize – размер одного блока в байтах;

q       fs_fsize — размер одного фрагмента в байтах;

q       fs_frag — кол-во фрагментов в блоке;

q       fs_fpg – размер каждой группы цилиндров, выраженный в блоках (так же может быть найден через fs_cgsize);

 

Для перевода смещений, выраженных в фрагментах, в номера секторов, служит следующая формула: sec_n(fragment_offset) = fragment_offset*(fs_bsize/fs_frag/512) или ее более короткая разновидность: sec_n(fragment_offset) = fragment_offset*fs_fsize /512;

Структура суперблока определена в файле /src/ufs/ffs/fs.h и в упрощенном виде выглядит так:

 

struct fs {

/* 0x00 */   int32_t      fs_firstfield;     /* historic file system linked list, */

/* 0x04 */   int32_t      fs_unused_1;        /*     used for incore super blocks */

/* 0x08 */   ufs_daddr_t fs_sblkno;            /* addr of super-block in filesys */

/* 0x0C */   ufs_daddr_t fs_cblkno;            /* offset of cyl-block in filesys */

/* 0x10 */   ufs_daddr_t fs_iblkno;            /* offset of inode-blocks in filesys */

/* 0x14 */   ufs_daddr_t fs_dblkno;            /* offset of first data after cg */

/* 0x18 */   int32_t      fs_cgoffset;       /* cylinder group offset in cylinder */

/* 0x1C */   int32_t      fs_cgmask;         /* used to calc mod fs_ntrak */

/* 0x20 */   time_t        fs_time;           /* last time written */

/* 0x24 */   int32_t      fs_size;           /* number of blocks in fs */

/* 0x28 */   int32_t      fs_dsize;          /* number of data blocks in fs */

/* 0x2C */   int32_t      fs_ncg;            /* number of cylinder groups */

/* 0x30 */   int32_t      fs_bsize;          /* size of basic blocks in fs */

/* 0x34 */   int32_t      fs_fsize;          /* size of frag blocks in fs */

/* 0x38 */   int32_t      fs_frag;           /* number of frags in a block in fs */

 

/* these are configuration parameters */

/* 0x3С */   int32_t      fs_minfree;        /* minimum percentage of free blocks */

/* 0x40 */   int32_t      fs_rotdelay;        /* num of ms for optimal next block */

/* 0x44 */   int32_t      fs_rps;            /* disk revolutions per second */

 

/* sizes determined by number of cylinder groups and their sizes */

/* 0x98 */   ufs_daddr_t fs_csaddr;            /* blk addr of cyl grp summary area */

/* 0x9C */   int32_t      fs_cssize;         /* size of cyl grp summary area */

/* 0xA0 */   int32_t      fs_cgsize;         /* cylinder group size */

 

/* these fields can be computed from the others */

/* 0xB4 */   int32_t      fs_cpg;            /* cylinders per group */

/* 0xB8 */   int32_t      fs_ipg;            /* inodes per group */

/* 0xBC */   int32_t      fs_fpg;            /* blocks per group * fs_frag */

 

/* these fields are cleared at mount time */

/* 0xD0 */   int8_t   fs_fmod;          /* super block modified flag */

/* 0xD1 */   int8_t   fs_clean;         /* file system is clean flag */

/* 0xD2 */   int8_t        fs_ronly;          /* mounted read-only flag */

/* 0xD3 */   int8_t   fs_flags;         /* see FS_ flags below */

/* 0xD4 */   u_char fs_fsmnt[MAXMNTLEN];      /* name mounted on */

};

Листинг 1 формат супер-блока (второстепенные поля опущены)

За концом супеблока, на некотором отдалении от него, находится первая группа цилиндров. В начале каждой группы расположена служебная структура cg (далее по тексту — описатель группы цилиндров, термин мой — КК), содержащая магическую последовательность 550209h по которую все уцелевшие группы можно найти даже при полностью испорченном супеблоке (штатным образом, стартовые адреса всех последующих групп вычисляются путем умножения номера группы на ее размер, содержащийся в поле fs_cgsize).

Другие важные параметры:

 

q       cg_cgx — порядковой номер группы, отсчитываемый от нуля;

q       cg_old_niblk — кол-во inode в данной группе;

q       cg_ndblk — кол-во блоков данных в данной группе;

q       csum — кол-во свободных inode и блоков данных в данной группе;

q       cg_iusedoff — смещение карты занятых inod'e, отсчитываемое от начала данной группы и измеряемое в байтах;

q       cg_freeoff — смещение карты свободного пространства (байты от начла группы);

 

Структура cg определена в файле /src/ufs/ffs/fs.h и выглядит следующим образом:

#define CG_MAGIC    0x090255

#define MAXFRAG     8

struct cg {

/* 0x00 */ int32_t  cg_firstfield;             /* historic cyl groups linked list */

/* 0x04 */ int32_t  cg_magic;                  /* magic number */

/* 0x08 */ int32_t  cg_old_time;        /* time last written */

/* 0x0С */ int32_t  cg_cgx;                    /* we are the cgx'th cylinder group */

/* 0x10 */ int16_t  cg_old_ncyl;        /* number of cyl's this cg */

/* 0x12 */ int16_t  cg_old_niblk;       /* number of inode blocks this cg */

/* 0x14 */ int32_t  cg_ndblk;                  /* number of data blocks this cg */

/* 0x18 */ struct  csum cg_cs;          /* cylinder summary information */

/* 0x28 */ int32_t  cg_rotor;                  /* position of last used block */

/* 0x2С */ int32_t  cg_frotor;          /* position of last used frag */

/* 0x30 */ int32_t  cg_irotor;          /* position of last used inode */

/* 0x34 */ int32_t  cg_frsum[MAXFRAG];  /* counts of available frags */

/* 0x54 */ int32_t  cg_old_btotoff;            /* (int32) block totals per cylinder */

/* 0x58 */ int32_t  cg_old_boff;        /* (u_int16) free block positions */

/* 0x5С */ int32_t  cg_iusedoff;        /* (u_int8) used inode map */

/* 0x60 */ int32_t  cg_freeoff;         /* (u_int8) free block map */

/* 0x64 */ int32_t  cg_nextfreeoff;            /* (u_int8) next available space */

/* 0x68 */ int32_t  cg_clustersumoff;          /* (u_int32) counts of avail clusters */

/* 0x6С */ int32_t  cg_clusteroff;             /* (u_int8) free cluster map */

/* 0x70 */ int32_t  cg_nclusterblks;           /* number of clusters this cg */

/* 0x74 */ int32_t  cg_niblk;                  /* number of inode blocks this cg */

/* 0x78 */ int32_t  cg_initediblk;             /* last initialized inode */

/* 0x7С */ int32_t  cg_sparecon32[3];          /* reserved for future use */

/* 0x00 */ ufs_time_t cg_time;          /* time last written */

/* 0x00 */ int64_t  cg_sparecon64[3];          /* reserved for future use */

/* 0x00 */ u_int8_t cg_space[1];        /* space for cylinder group maps */

 /* actually longer */

Листинг 2 структура описателя группы цилиндров

Между описателем группы цилиндров и группой inode расположена карта занятых inode и карта свободного дискового пространства, представляющие собой обыкновенные битовые поля, точно такие же как и в NTFS. При восстановлении удаленных файлов без этих карт никуда! Отделяя зерна от плевел, они существенно сужают круг поиска, что особенно хорошо заметно на дисках, заполненных более чем наполовину.

За картами следует массив inod'ов, смещение которого содержится в поле cg_iusedoff (адрес первой группы inode продублирован в суперблоке). По сути, в UFS структура inode ничем не отличается от ext2fs, только расположение полей другое. К тому же имеется только один блок косвенной адресации вместо трех, но это уже детали, в которые не будет углубляться (иначе или зависнем или завязнем), а лучше рассмотрим назначение фундаментальных полей, к числу которых принадлежат:

 

q       di_nlink — кол-во ссылок на файл (0 означает "удален");

q       di_size — размер файла в байтах;

q       di_atime/di_atimensec — время последнего доступа к файлу;

q       di_mtime/di_mtimensec — время последней модификации;

q       di_ctime/di_ctimensec – время последнего изменения inode;

q       di_db – адреса первых 12-блоков данных файла, отсчитываемые в фрагментах от начала группы цилиндров;

q       di_ib — адрес блоков косвенной адресации (фрагменты от начала группы);

 

Сама структура inode определена в файле /src/ufs/ufs/dinode.h и для UFS1 выглядит так:

 

struct dinode {

/* 0x00 */   u_int16_t    di_mode;     /*   0: IFMT, permissions; see below. */

/* 0x02 */   int16_t             di_nlink;    /*   2: File link count. */

/* 0x04 */   union {

             u_int16_t oldids[2];              /*   4: Ffs: old user and group ids. */

             int32_t        inumber;          /*   4: Lfs: inode number. */

       } di_u;

/* 0x08 */   u_int64_t    di_size;     /*   8: File byte count. */

/* 0x10 */   int32_t             di_atime;    /*  16: Last access time. */

/* 0x14 */   int32_t             di_atimensec; /*  20: Last access time. */

/* 0x18 */   int32_t             di_mtime;    /*  24: Last modified time. */

/* 0x1C */   int32_t             di_mtimensec; /*  28: Last modified time. */

/* 0x20 */   int32_t             di_ctime;    /*  32: Last inode change time. */

/* 0x24 */   int32_t             di_ctimensec; /*  36: Last inode change time. */

/* 0x28 */   ufs_daddr_t  di_db[NDADDR];      /*  40: Direct disk blocks. */

/* 0x58 */   ufs_daddr_t  di_ib[NIADDR];      /*  88: Indirect disk blocks. */

/* 0x64 */   u_int32_t    di_flags;    /* 100: Status flags (chflags). */

/* 0x68 */   int32_t             di_blocks;   /* 104: Blocks actually held. */

/* 0x6C */   int32_t             di_gen;             /* 108: Generation number. */

/* 0x70 */   u_int32_t    di_uid;             /* 112: File owner. */

/* 0x74 */   u_int32_t    di_gid;             /* 116: File group. */

/* 0x78 */   int32_t             di_spare[2]; /* 120: Reserved; currently unused */

};

Листинг 3 структура inode в USF1


Рисунок 3 схематичное изображение inode

В UFS2 формат inode был существенно изменен — появилось множество новых полей, удвоилась ширина адресных полей и т. д. Что это обозначает для нас в практическом плане? Смещения всех полей изменились, только и всего, а общий принцип работы с inod'ами остался прежним:

 

struct ufs2_dinode {

/* 0x00 */ u_int16_t       di_mode;     /*   0: IFMT, permissions; see below. */

/* 0x02 */ int16_t  di_nlink;    /*   2: File link count. */

/* 0x04 */ u_int32_t       di_uid;             /*   4: File owner. */

/* 0x08 */ u_int32_t       di_gid;             /*   8: File group. */

/* 0x0C */ u_int32_t       di_blksize;  /*  12: Inode blocksize. */

/* 0x10 */ u_int64_t       di_size;     /*  16: File byte count. */

/* 0x18 */ u_int64_t       di_blocks;   /*  24: Bytes actually held. */

/* 0x20 */ ufs_time_t      di_atime;    /*  32: Last access time. */

/* 0x28 */ ufs_time_t      di_mtime;    /*  40: Last modified time. */

/* 0x30 */ ufs_time_t      di_ctime;    /*  48: Last inode change time. */

/* 0x38 */ ufs_time_t      di_birthtime; /*  56: Inode creation time. */

/* 0x40 */ int32_t  di_mtimensec; /*  64: Last modified time. */

/* 0x44 */ int32_t  di_atimensec; /*  68: Last access time. */

/* 0x48 */ int32_t  di_ctimensec; /*  72: Last inode change time. */

/* 0x4C */ int32_t  di_birthnsec; /*  76: Inode creation time. */

/* 0x50 */ int32_t  di_gen;             /*  80: Generation number. */

/* 0x54 */ u_int32_t       di_kernflags; /*  84: Kernel flags. */

/* 0x58 */ u_int32_t       di_flags;    /*  88: Status flags (chflags). */

/* 0x5C */ int32_t  di_extsize;  /*  92: External attributes block. */

/* 0x60 */ ufs2_daddr_tdi_extb[NXADDR];/*  96: External attributes block. */

/* 0x70 */ ufs2_daddr_tdi_db[NDADDR];   /* 112: Direct disk blocks. */

/* 0xD0 */ ufs2_daddr_tdi_ib[NIADDR];   /* 208: Indirect disk blocks. */

/* 0xE8 */ int64_t  di_spare[3]; /* 232: Reserved; currently unused */

 };

Листинг 4 структура inode в USF2

Имена файлов хранятся в директориях. В inod'ах их нет. С точки зрения UFS, директории являются обыкновенными файлами (ну, может, не совсем обыкновенными) и могут хранится в любом месте, принадлежащем группе цилиндров. Файловая система UFS поддерживает несколько типов хеширования директорий, однако на структуре хранения имен это никак не отражается. Имена хранятся в блоках, называемых DIRBLKSIZ в структурах типа direct, выровненных по 4'х байтовой границе.

Структура direct определена в файле /src/ufs/ufs/dir.h и содержит: номер inode, описывающий данный файл, тип файла, его имя, а так же длину самой структуры direct, используемую для нахождения следующего direct'а в блоке.

 

struct  direct {

/* 0x00 */ u_int32_t       d_ino;              /* inode number of entry */

/* 0x04 */ u_int16_t       d_reclen;           /* length of this record */

/* 0x06 */ u_int8_t d_type;                    /* file type, see below */

/* 0x07 */ u_int8_t d_namlen;           /* length of string in d_name */

/* 0x08 */ char     d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */

};

Листинг 5 структура direct, отвечающая за хранение имен файлов и директорий

На этом описание файловой системы UFS можно считать законченным. Для ручного восстановления данных приведенной информации вполне достаточно.

Комментариев нет:

Отправить комментарий