Inode и каталоги

В традиционной файловой системе (ФС) Unix доступный объём физического носителя делится между блоками данных и областью хранения метаданных - Inode. Количество Inode определяет максимальное число объектов (файлов, каталогов, сокетов и т.п) которые может хранить ФС. При форматировании ФС. Необходимо соблюдать баланс между данными и метаданными. При заполнении ФС мелкими файлами возможна ситуация, когда при свободной области данных исчерпывается запас Inode и создание новых файлов становится невозможным.

Просмотр числа inode в ФС - команда df -i. Просмотр числа блоков данных команда df

Суперблок

Суперблок хранит информацию о "геометрии" файловой системы: размещение таблицы inode на диске, число inode, число свободных inode, размещение начала блоков данных, число блоков данных, число свободных блоков и т.п.

Битовые карты

Битовые карты блоков данных и inode помечают занятые блоки/inode. 0 - элемент свободен, 1 - занят.

Inode

C каждым файлом в ОС Unix связана особая структура данных - индексный дескриптор (inode), хранящий метаинформацию файла (владелец, права доступа и т.п.).

Индексные дескрипторы в оригинальной UnixFS объединялись в последовательно нумерованный (индексированный) массив, что и дало название структуре. В современных ФС эта структура может иметь разные размеры и набор полей или отсутствовать вовсе. Соответственно, классические утилиты мониторинга ФС могут выдавать неверные данные о количестве занятых и свободных inode.

В реализации API доступа к ФС inode – это стандартизованная структура данных для обобщённого представления атрибутов файла. В оперативной памяти индексный дескриптор может быть представлен в виртуальном виде – vnode. Для ФС , не хранящих индексные дескрипторы, vnode создаётся на основе других подходящих источников данных.

Номер индексного дескриптора уникален в рамках одной ФС, однако, при монтировании нескольких ФС в одно дерево номера индексных дескрипторов будут повторяться. Поэтому vnode хранит номер индексного дескриптора плюс идентификатор ФС, в которой он находится. Для дисковых ФС в Linux номером ФС является число, составленное из мажора и минора блочного устройства, на котором ФС расположена. Для NFS, похоже, номер ФС определяется порядком монтирования и последовательно возрастает начиная с 1Ah.

Блоки данных

Размещение данных файла в блоках описывается ссылками, хранящимися в inode файла

Прикрепленный файлРазмер
Иконка изображения Структура оригинальной UFS16.03 КБ

Оптимизация дискового доступа в FFS

Оригинальная файловая система Unix (UFS) имела несколько явных недостатков:

  • Суперблок был единичной точкой отказа. Его физическое разрушение приводило к полной потере доступа к данным на диске.
  • Inode и блоки данных хранились на разных дорожках, что требовало непрерывных перемещений магнитной головки при доступе к файлам.

Для решения этих проблем в Быстрой файловой системе (FFS) дорожки диска были разбиты на группы, каждая из которых имела структуру полной файловой системы. Таким образом суперблок дублировался во всех группах, а inode и блоки данных внутри группы размещались на соседних дорожках.

Подобное деление на группы сохранилась и в семействе ФС Extfs в Linux.

Прикрепленный файлРазмер
Иконка изображения Размещение структур FFS в группах дорожек на диске53.71 КБ

Каталоги

Каталоги

Древовидную структуру файловой системы в Unix обеспечивают каталоги, которые хранят таблицу соответствий Имя->inode. В этой таблице требуется уникальность имен, но не уникальность номеров inode. Благодаря этому, каждый объект ФС может иметь несколько имён. Счётчик имён хранится в inode объекта.

В Unix отсутствует операция удаления объекта из ФС. Есть только операция удаления имени из каталога - unlink. Объект, у которого нет имён и который не открыт ни одним процессом, удаляется автоматически.

У каталогов есть одно "нормальное" имя, имя '.' в самом каталоге и имя '..' в каждом из подкаталогов. В Linux другие имена для каталога создать нельзя. Нарушение этого правила привело к тому, что в структуре файловой системы могли бы образоваться циклы, а это бы нарушило работу алгоритмов обхода дерева каталогов.

У других типов объектов (файлов, FIFO, файлов устройств, сокетов, символических ссылок) может быть много имен в одном или в нескольких каталогах. Такие имена называют "жёсткими ссылками" (hard links), поскольку они гарантированно ссылаются на существующий inode. Поскольку нумерация Inode в каждой файловой системе своя, жёсткие ссылки могут указывать только на объекты в той же файловой системе, что и каталог, в котором они опубликованы.

В противоположность этому, символические или "мягкие" ссылки (symlinks, soft links) - это особые объекты файловой системы, которые хранят, вообще говоря, произвольные текстовые строки, интерпретируемые, как пути к файлам. Мягкие ссылки могут ссылаться на несуществующие объекты и не отражаются в счетчике Inode.

В Linux обычные пользователи могут создавать жёсткие ссылки лишь на объекты, владельцами которых они являются, поскольку это требует права на запись в счётчик имён в inode.

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

Прикрепленный файлРазмер
Иконка изображения Каталог в Unix11.13 КБ
Иконка изображения Запись в каталоге файловой системы FFS8.55 КБ

Выделение блоков данных под каталоги в ext2fs

При создании каталога ему выделяется 0 блоков данных и 152 байта внутри inode.

До тех пор, пока новые записи direntry помещаются в 152 байта (7 записей с именами короче 4 байт) блоки данных не выделяются. После превышения размера в 152 байта начинают выделяться блоки данных. Блоки выделяются с запасом, чтобы обеспечить отсутствие фрагментации.

При создании новой записи direntry происходит просмотр каталога блок за блоком в поисках уже существующей записи direntry в хвосте которой есть свободное место для создания новой direntry.

Если свободное место не обнаружено, то размер каталога увеличивается на один блок и новая запись direntry занимает новый блок целиком.

При удалении записей direntry из каталога блоки данных никогда не освобождаются, но могут переиспользоваться, путём размещения новых direntry на свободных местах, оставшихся после удалённых direntry.

Последняя запись direntry в блоке всегда имеет такой размер, чтобы запомнить блок до конца, поэтому размер каталога всегда кратен размеру блока.

Структура Inode

Атрибуты файла, хранящиеся в vnode, могут быть получены вызовом stat/fstat/lstat, который возвращает структуру данных

dev;      /* устройство */
ino;      /* индексный дескриптор */

mode;     /* режим доступа */

nlink;    /* количество жестких ссылок */

uid;      /* идентификатор пользователя-владельца */
gid;      /* идентификатор группы-владельца */

rdev;     /* тип устройства */
          /* (если это устройство) */

size;     /* общий размер в байтах */
blocks;   /* количество выделенных блоков */
blksize;  /* размер блока ввода-вывода */
          /* в файловой системе */

atime;    /* время последнего доступа */
mtime;    /* время последней модификации */
ctime;    /* время последнего изменения */

Из командной строки просмотреть атрибуты можно командой stat

$ stat /bin/ping
   File: `/bin/ping'
   Size: 40760           Blocks: 88         IO Block: 4096   regular file
 Device: fd00h/64768d    inode: 113377318   Links: 1
 Access: (4755/-rwsr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
 Access: 2014-03-17 08:38:31.000000000 +0600
 Modify: 2011-05-21 03:08:42.000000000 +0600
 Change: 2012-02-02 11:17:13.000000000 +0600

Метки времени

С каждым объектом ФС Unix связаны три метки времени:

  • ctime – время модификации атрибутов файла (inode). Изменяется при смене прав доступа, владельца или группы файла, а также при изменении содержимого файла.
  • mtime – время последней записи в файл.
  • atime – время последнего доступа к файлу. В зависимости от опций ФС может оставаться неизменным (оптимизация), изменяться при открытии файла, изменяться каждые n-секунд пока файл остаётся открытым. Вызов stat не меняет atime.

Пользователь может произвольно изменить mtime и atime, в том числе на прошлое или будущее время (например, touch -t 200012311800 time.txt). При этом ctime изменится на момент выполнения операции.

Метка времени создания файлов в классическом Unix отсутствует, что довольно неудобно для администраторов.

В конкретных реализациях ФС могут быть и другие метки, но они недоступны через стандартные функции API. Например, в ex2fs есть поле для хранения времени удаления файла dtime, а в Sun StorEdge QFS хранится время создания файла creation time.

Права доступа на файлы и каталоги

В Unix права доступа к объекту ФС хранятся в битовом поле индексного дескриптора (inode). Шестнадцатиразрядное битовое поле, называемое mode, включает в себя четыре бита, определяющие тип объекта, три бита особых признаков (suid, sgid, sticky) и девять бит прав доступа. Правила интерпретации флагов mode (особенно suid, sgid, sticky) и правила манипуляции ими могут отличаться в разных ОС. В данном тексте описываются правила Linux.

Менять права доступа (записывать в inode) может владелец файла или администратор. Члены группы файла никаких особых прав на inode не имеют. Пользователь может отобрать у себя собственные права на чтение и запись в файл, но право на запись в inode (в т.ч. право на смену прав) сохраняется у владельца файла при любых обстоятельствах. Пользователь не может передать право собственности на файл другому пользователю и не может забрать право собственности на файл у другого пользователя.

При смене владельца или группы флаги suid и sgid сбрасываются. Пользователь не может установить флаг sgid на собственный файл, если файл принадлежит группе, в которую сам пользователь не входит.

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

  1. Файлы, создаваемые в каталоге, которому назначен флаг sgid, наследуют группу, к которой принадлежит каталог, даже если создатель файла в эту группу не входит.
  2. Файлы, создаваемые программой, исполняемому файлу которой назначен флаг sgid, получают группу, которой принадлежит файл программы.

Права доступа включают право на чтение (R__ead), запись (__W__rite) и исполнение (e__X__ecute). Существует три набора прав __rwx для владельца файла (U__ser), группы файла (__G__roup) и остальных (__O__ther). Традиционно права записываются в виде строки из трёх троек __rwx. Тройки расположены слева направо в порядке ugo. Отсутствующее право помечается прочерком. Набор прав также может быть представлен в виде трёхзначного восьмеричного числа, в котором 1 соответствует наличию права, а 0 отсутствию. Например, rwxr-xr-- эквивалентно 7548. Утилита stat позволяет выдать права доступа к файлу в восьмеричном виде путём задания формата %a. Например, для каталога /tmp установлены все права для всех и sticky bit:

$> stat --format=%a /tmp
1777

Права доступа проверяются в момент выполнения системных вызовов, связанных с доступом к файлам и каталогам, таких как creat(), open(), unlink(), exec(). Из трёх наборов прав выбирается тот, который наиболее точно характеризует пользователя, пытающегося получить доступ к файлу. Права для владельца перекрывают ему права для группы и прочих, для остальных членов группы права для группы перекрывают права для прочих.

Права доступа к файлам

Для файла права rw проверяются в момент выполнения вызова ядра open(). При этом права доступа сверяются с флагами доступа, передаваемыми в open. Право x проверяется в момент выполнения вызова exec(). Для выполнения двоичных файлов право на чтение не обязательно. Защищенные от чтения исполняемые файлы нельзя запустить под отладчиком. Для скриптов запуск означает запуск программы интерпретатора, которая получает в качестве первого параметра имя файла скрипта. В этом случае интерпретатор должен открыть файл скрипта на чтение и право r необходимо.

Флаги suid и sgid в сочетании с правом на исполнение изменяют эффективные права процесса в момент выполнения программы из этого файла вызовом exec. Эффективные права соответствуют владельцу (группе владельцев) файла. При наличии права на исполнение suid и sgid отображаются буквой s в позиции флага права на исполнение. Флаг suid (sgid) может быть назначен файлу, не имеющему флага права на выполнение для владельца (группы). При отсутствии права на исполнение suid и sgid отображаются буквой S в позиции флага права на исполнение. Флаг suid без права на выполнение владельцем ни на что не влияет. Флаг sgid без права на выполнение группой используется как признак принудительной блокировки файла при выполнении системного вызова fcntl(fd, F_SETLK,...).

Флаг sticky bit не оказывает в Linux влияния на работу с файлом. Более того, системный вызов chmod() молча игнорирует попытки установить sticky bit, не выдавая ошибки, но и не выполняя действия. В старых версиях Unix sticky bit в сочетании с флагом прав на исполнение указывал, что после завершения программы её код должен быть сохранён в области свопа для быстрого повторного запуска.

Права доступа к каталогам

Каталог можно представить как таблицу, содержащую много записей, каждая из которых состоит из двух полей: имя и номер индексного дескриптора. В этой модели право на запись в каталог означает право на создание и удаление записей, т.е. создание файлов в каталоге, создание новых имен для существующих файлов (link), удаление имен файлов (возможно вместе с файлами) (unlink). Для удаления файла нет необходимости иметь право на операции с файлом, достаточно иметь право на запись в каталог, в котором хранится его последнее имя.

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

sticky бит используется для каталогов, запись в которые разрешена группе или остальным. Данный бит указывает на то, что создавать записи в каталоге может любой, имеющий право на запись, а удалять только владелец объекта, на который указывает запись или владелец каталога. sTicky бит обозначается буквой t в позиции права на исполнение для остальных, если само право есть, и буквой T, если такого права нет.

Флаг setgid, установленный на каталог, приводит к тому, что все объекты, создаваемые в этом каталоге, наследуют группу каталога. Создаваемые подкаталоги дополнительно наследуют сам бит setgid.

Флаг setuid, установленный на каталог в System V и Linux, игнорируется. В BSD системах setuid, установленный на каталог, действует аналогично setgid.

ACL

Традиционные права доступа, хранящиеся в поле mode, не позволяют задать права доступа с точностью до пользователя или до группы. Скажем, нельзя распределить права доступа так, чтобы пользователь user1 имел право только на чтение, user2 - только на запись, а user3 - только на исполнение.

Для преодоления подобных ограничений современные реализации Unix поддерживают списки доступа (Access Lists) - ACL. Для хранения списков доступа может резервироваться отдельный inode, что позволяет выделять для них место в области данных ФС, не создавая при этом отдельного видимого файла.

Списки доступа состоят из записей, содержащих тип записи (пользователь, группа, остальные, маска), идентификатор пользователя или группы, флаги прав на чтение, запись и исполнение. Права доступа, содержащиеся в inode обязательно дублируются тремя записями в ACL - владелец, группа, остальные. Маска определяет максимальные права, которые будут доступны через ACL. Если дать кому-либо права rwx, а маска равна r--, то результатом будет право r--. Действие маски не распространяется на владельца файла и на остальных.

Для индикации наличия ACL информационные утилиты добавляют символ + после стандартного списка прав доступа

Команда просмотра ACL

$ getfacl /etc
getfacl: Removing leading '/' from absolute path names
# file: etc
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

Команда изменения acl

setfacl -m u:lisa:r file

устанавливает право на чтение file для пользователя lisa

setfacl -x u:lisa:r file

отбирает право на чтение file для пользователя lisa

Capabilities

В Linux кроме повышения прав программы через флаг setuid, возможно повышений прав на отдельные привилегированные функции - capabilities.

Команда просмотра capabilities

$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p

Команда установки capabilities

setcap capabilities filename

Формат capabilities описан в man cap_from_text.

Макросы для выделения отдельных флагов

identifier value comment
S_IFMT F000 format mask
S_IFSOCK A000 socket
S_IFLNK C000 symbolic link
S_IFREG 8000 regular file
S_IFBLK 6000 block device
S_IFDIR 4000 directory
S_IFCHR 2000 character device
S_IFIFO 1000 fifo
S_ISUID 0800 SUID
S_ISGID 0400 SGID
S_ISVTX 0200 sticky bit
S_IRWXU 01C0 user mask
S_IRUSR 0100 read
S_IWUSR 0080 write
S_IXUSR 0040 execute
S_IRWXG 0038 group mask
S_IRGRP 0020 read
S_IWGRP 0010 write
S_IXGRP 0008 execute
S_IRWXO 0007 other mask
S_IROTH 0004 read
S_IWOTH 0002 write
S_IXOTH 0001 execute

Ссылки:

http://www.softpanorama.org/Access_control/Permissions/suid_attribute.shtml

http://www.het.brown.edu/guide/chmod.html

Структуры данных ext2fs

С логической точки зрения ФС образуется за счёт двух элементов: массива индексных дескрипторов и системы каталогов, связывающих имена файлов с номерами индексных дескрипторов. Индексные дескрипторы хранят метаинформацию файлов и ссылки на блоки данных файлов. Каталоги объединены в дерево с двунаправленной системой ссылок между узлами.

Дисковое пространство в ext2fs разбивается на логические блоки размером 1, 2 или 4 КБ. Блоки используются под хранение нескольких служебных структур, массива индексных дескрипторов и, собственно, под хранение содержимого файлов. Для оптимизации времени доступа блоки поделены на группы. По возможности индексный дескриптор файла и его данные размещаются в пределах одной группы, что снижает время на перемещение головок по диску.

В одном из начальных блоков (со смещением 1024 байта от начала раздела) размещается Суперблок – структура данных размером 1024 байта, описывающая основные настраиваемые параметры ФС. В этих параметрах задаётся размер блока, количество индексных дескрипторов, количество блоков, отведенных под хранение данных и т.п. Размер блока влияет на потери дискового пространства в "хвостах" файлов (больше блок – больше потери) и на максимальную длину файла (больше блок – больше максимальная длина). Количество индексных дескрипторов определяет максимальное число объектов, которые могут быть размещены в данной ФС.

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

Структура группы блоков в ext2fs

Суперблок Массив дескрипторов групп Карта свободных блоков Карта свободных индексных дескрипторов Массив индексных дескрипторов Блоки данных
Дублируются во всех группах блоков для надёжности Данные, индивидуальные для каждой группы

Суперблок

поле описание
s_inodes_count Число индексных дескрипторов во всей ФС
s_blocks_count Число блоков, отведённых под ФС
s_r_blocks_count Число зарезервированных блоков данных
s_free_blocks_count Число свободных блоков данных
s_free_inodes_count Число свободных индексных дескрипторов
s_first_data_block Адрес первого блока данных
s_log_block_size Размер блока
s_log_frag_size
s_blocks_per_group Число блоков в группе
s_frags_per_group
s_inodes_per_group Число индексных дескрипторов в группе
s_mtime Время последнего монтирования
s_wtime Время последней записи
s_mnt_count Количество монтирований
s_max_mnt_count Количество монтирований без проверки на ошибки
s_magic Магическое число ex2fs
s_state Флаг "чистого" выключения
....
s_reserved[235] дополнение до 1024 байтов

Дескриптор группы

поле описание
bg_block_bitmap Адрес битовой карты свободных блоков
bg_inode_bitmap Адрес битовой карты свободных индексных дескрипторов
bg_inode_table Адрес таблицы индексных дескрипторов
bg_free_blocks_count Количество свободных блоков в группе
bg_free_inodes_count Количество свободных индексных дескрипторов в группе
bg_used_dirs_count Количество каталогов группе (для fsck, например)
bg_pad выравнивание до удобного размера

Поля индексного дескриптора

поле описание
i_mode Тип, suid, sgid, sticky, права доступа
i_uid Владелец
i_size Размер
i_atime Access time
i_ctime Creation time
i_mtime Modification time
i_dtime Deletion Time
i_gid Группа
i_links_count Число имён
i_blocks Число занимаемых блоков
i_flags Флаги
i_reserved1
i_block[15] Указатели на блоки данных
i_version Версия (для NFS)
i_file_acl File ACL
i_dir_acl Directory ACL
.... прочее, дополненное до удобного размера

Указатели на блоки данных

номер описание
1 адрес блока или 0
... ...
12 адрес блока или 0
13 адрес блока косвенной адресации или 0
14 адрес блока двойной косвенной адресации или 0
15 адрес блока тройной косвенной адресации или 0

Блоки адресуются с единицы. Ноль в указателе означает, что блок не выделялся.

Зарезервированные номера индексных дескрипторов

идентификатор номер Описание
EXT2_BAD_INO 1 Сбойные блоки
EXT2_ROOT_INO 2 Корневой каталог
EXT2_ACL_IDX_INO 3 ACL (списки доступа)
EXT2_ACL_DATA_INO 4 ACL (списки доступа)
EXT2_BOOT_LOADER_INO 5 Загрузчик
EXT2_UNDEL_DIR_INO 6 Каталог для восстановления стёртых файлов
EXT2_FIRST_INO 11 Первый нормальный inode. Часто занят каталогом lost+found

Запись в каталоге

поле описание
inode Номер индексного дескриптора
rec_len Длина записи
name_len Длина имени файла
name Имя файла (переменной длины до 255 символов)

Простая программа для проверки максимальной длины имени

 F=""
 for I in {1..1024};do
      F=${F}Z
      if touch $F; then
          rm $F
      else
          echo "Maximum name length="$((I-1))
          break
      fi
  done  2>/dev/null

Ссылки

http://uranus.chrysocome.net/explore2fs/es2fs.htm