В традиционной файловой системе (ФС) Unix доступный объём физического носителя делится между блоками данных и областью хранения метаданных - Inode. Количество Inode определяет максимальное число объектов (файлов, каталогов, сокетов и т.п) которые может хранить ФС. При форматировании ФС. Необходимо соблюдать баланс между данными и метаданными. При заполнении ФС мелкими файлами возможна ситуация, когда при свободной области данных исчерпывается запас Inode и создание новых файлов становится невозможным.
Просмотр числа inode в ФС - команда df -i
. Просмотр числа блоков данных команда df
Суперблок хранит информацию о "геометрии" файловой системы: размещение таблицы inode на диске, число inode, число свободных inode, размещение начала блоков данных, число блоков данных, число свободных блоков и т.п.
Битовые карты блоков данных и inode помечают занятые блоки/inode. 0 - элемент свободен, 1 - занят.
C каждым файлом в ОС Unix связана особая структура данных - индексный дескриптор (inode), хранящий метаинформацию файла (владелец, права доступа и т.п.).
Индексные дескрипторы в оригинальной UnixFS объединялись в последовательно нумерованный (индексированный) массив, что и дало название структуре. В современных ФС эта структура может иметь разные размеры и набор полей или отсутствовать вовсе. Соответственно, классические утилиты мониторинга ФС могут выдавать неверные данные о количестве занятых и свободных inode.
В реализации API доступа к ФС inode – это стандартизованная структура данных для обобщённого представления атрибутов файла. В оперативной памяти индексный дескриптор может быть представлен в виртуальном виде – vnode. Для ФС , не хранящих индексные дескрипторы, vnode создаётся на основе других подходящих источников данных.
Номер индексного дескриптора уникален в рамках одной ФС, однако, при монтировании нескольких ФС в одно дерево номера индексных дескрипторов будут повторяться. Поэтому vnode хранит номер индексного дескриптора плюс идентификатор ФС, в которой он находится. Для дисковых ФС в Linux номером ФС является число, составленное из мажора и минора блочного устройства, на котором ФС расположена. Для NFS, похоже, номер ФС определяется порядком монтирования и последовательно возрастает начиная с 1Ah.
Размещение данных файла в блоках описывается ссылками, хранящимися в inode файла
Прикрепленный файл | Размер |
---|---|
Структура оригинальной UFS | 16.03 КБ |
Оригинальная файловая система Unix (UFS) имела несколько явных недостатков:
Для решения этих проблем в Быстрой файловой системе (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.
Для оптимизации операции удаления файлов в структуре каталога хранится размер записи и длина имени. При удалении записи из каталога она фактически остаётся на прежнем месте. Поле общей длины предыдущей записи увеличивается на размер удалённой записи. При создании новых записей переиспользуется место, оставшееся от удалённых записей.
Прикрепленный файл | Размер |
---|---|
Каталог в Unix | 11.13 КБ |
Запись в каталоге файловой системы FFS | 8.55 КБ |
При создании каталога ему выделяется 0 блоков данных и 152 байта внутри inode.
До тех пор, пока новые записи direntry помещаются в 152 байта (7 записей с именами короче 4 байт) блоки данных не выделяются. После превышения размера в 152 байта начинают выделяться блоки данных. Блоки выделяются с запасом, чтобы обеспечить отсутствие фрагментации.
При создании новой записи direntry происходит просмотр каталога блок за блоком в поисках уже существующей записи direntry в хвосте которой есть свободное место для создания новой direntry.
Если свободное место не обнаружено, то размер каталога увеличивается на один блок и новая запись direntry занимает новый блок целиком.
При удалении записей direntry из каталога блоки данных никогда не освобождаются, но могут переиспользоваться, путём размещения новых direntry на свободных местах, оставшихся после удалённых direntry.
Последняя запись direntry в блоке всегда имеет такой размер, чтобы запомнить блок до конца, поэтому размер каталога всегда кратен размеру блока.
Атрибуты файла, хранящиеся в 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 связаны три метки времени:
Пользователь может произвольно изменить 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 на собственный файл, если файл принадлежит группе, в которую сам пользователь не входит.
При создании файла ему всегда назначается основная группа владельца. В дальнейшем владелец может назначить файлу группу, в которую входит сам. Существует как минимум два исключения из этих правил:
Права доступа включают право на чтение (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.
Традиционные права доступа, хранящиеся в поле 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
В 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
С логической точки зрения ФС образуется за счёт двух элементов: массива индексных дескрипторов и системы каталогов, связывающих имена файлов с номерами индексных дескрипторов. Индексные дескрипторы хранят метаинформацию файлов и ссылки на блоки данных файлов. Каталоги объединены в дерево с двунаправленной системой ссылок между узлами.
Дисковое пространство в ext2fs разбивается на логические блоки размером 1, 2 или 4 КБ. Блоки используются под хранение нескольких служебных структур, массива индексных дескрипторов и, собственно, под хранение содержимого файлов. Для оптимизации времени доступа блоки поделены на группы. По возможности индексный дескриптор файла и его данные размещаются в пределах одной группы, что снижает время на перемещение головок по диску.
В одном из начальных блоков (со смещением 1024 байта от начала раздела) размещается Суперблок – структура данных размером 1024 байта, описывающая основные настраиваемые параметры ФС. В этих параметрах задаётся размер блока, количество индексных дескрипторов, количество блоков, отведенных под хранение данных и т.п. Размер блока влияет на потери дискового пространства в "хвостах" файлов (больше блок – больше потери) и на максимальную длину файла (больше блок – больше максимальная длина). Количество индексных дескрипторов определяет максимальное число объектов, которые могут быть размещены в данной ФС.
Для повышения надёжности Суперблок дублируется в начале каждой группы. За ним следует массив дескрипторов групп, который также дублируется во всех группах. Далее идут битовые карты свободных индексных дескрипторов и свободных блоков данных группы. Эти битовые карты нужны для быстрого создания файлов и быстрого выделения блоков хранения данных. Далее находятся область хранения индексных дескрипторов и область хранения данных.
Суперблок | Массив дескрипторов групп | Карта свободных блоков | Карта свободных индексных дескрипторов | Массив индексных дескрипторов | Блоки данных |
---|---|---|---|---|---|
Дублируются во всех группах блоков для надёжности | Данные, индивидуальные для каждой группы |
поле | описание |
---|---|
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