Инструменты пользователя

Инструменты сайта


igor:udev_i_ego_primenenie_dlja_montirovanija_fajlovoj_sistemy

Все мои статьи: Статьи Игоря Романова

Udev и его применение для монтирования файловой системы

Необходимость и сущность операции монтирования, в сопоставлении Linux-Windows

Прежде чем рассматривать операцию монтирования, представим себе организацию файлов в операционных системах семейства DOS/Windows: там никакого монтирования нет.

Немного истории с предысторией

Системы DOS/Windows, как мы помним, выросли из древней системы CP/M, в которой предусматривалось всего два флоппи-дисковода A: и B:, и на каждом из них был единственный каталог файлов, так что полное имя файла включало букву дисковода и собственно имя.
Впоследствии появилась DOS, в которой был реализован принцип многоуровневого вложения каталогов. Также стало возможно добавить к компьютеру «винчестер», а позже CD-диски и флэш-накопители. Но принцип формирования общего файлового пространства остался прежним: каждому устройству, хранящему данные в виде файловой системы, ставится в соответствие очередная буква алфавита, и путь к файлу начинается с этой буквы. Файловая система на одном каком-то устройстве имеет вид дерева каталогов, а если устройств несколько, то образуется целый лес.
Такой подход имеет ряд недостатков, прежде всего: буква, присваиваемая устройству («диску»), не всегда постоянна. Она зависит от того, какие еще устройства имеются в системе и в каком порядке они подключались. Это затрудняет написание скриптов (хотя Windows - вообще малопригодная для скриптизации система). Кроме того, при таком подходе затруднено структурирование «файлового хозяйства». А теперь представим ситуацию из реальной жизни: на предприятии имеется компьютер, на котором установлена какая-то прикладная программа, и она сконфигурирована так, что ищет нужные ей файлы на определенном диске, допустим F:. В один прекрасный день с компьютером что-то происходит, и наш запасливый сисадмин, вместо того чтобы переустановить систему на имеющийся диск, заменяет винчестер другим, заранее заготовленным, со всем нужным софтом… но с другим количеством разделов. Или в связи с расширением производства нужно оборудовать второе рабочее место, оснащенное этой же прикладной программой, но на этом компьютере нет никаких других программ, так что не нужно такое большое количество разделов… Как минимум в таких ситуациях потребуется определенная работа по конфигурации нашей прикладной программы, а кто этим должен заниматься? И потом, одно дело офисные программы, которые, не найдя в нужном месте нужный файл, хотя бы как-то сигнализируют об этом. А вот производственно-управленческая программа в такой ситуации может молча сгенерировать отсутствующий файл заново, «забыв» о тех изменениях, которые были сделаны пользователем…

Как это делается у нас

В операционных системах семейства UNIX (к которому относится и Linux) практикуется иной подход. У нас файлы и каталоги образуют не лес, а единственное дерево, имеющее единственный корневой каталог. Это дерево собирается (монтируется) из деревьев, находящихся на разных дисках. Сразу вспоминается английский детский стишок: «Кабы реки и озера слить бы в озеро одно, а из всех деревьев бора сделать дерево одно…» При таком подходе буквы дисков вообще не нужны, и путаница с ними исключается, и ограниченность их количества над нами не висит. Файловая система, находящаяся на том или ином устройстве, занимает в общем файловом пространстве свое строго определенное место, обусловленное той ролью, которую играют эти файлы в решении задач на компьютере. Это место - один из каталогов в общем дереве - называется точкой монтирования. В наших системах принято, чтобы каталог, служащий точкой монтирования, существовал на момент монтирования.
Теперь представим, что у нас имеется компьютер, и мы к нему подключаем новое файловое устройство - допустим, просто USB-флэшку (хотя многие гаджеты, типа сотовых телефонов и фотоаппаратов, ведут себя аналогично). В любой ОС есть некоторые компоненты (как они называются, мы сейчас обсуждать не будем), которые следят за состоянием внешних разъемов компьютера и определяют факт добавления нового устройства. Эти компоненты регистрируют событие добавления устройства и докладывают об этом событии другим компонентам системы. Для иллюстрации того, как происходят события, дадим такую команду (смысл ее опишем ниже):

udevadm monitor –property –kernel –udev

До поры до времени никакого результата мы не увидим: udevadm monitor ждет события. Теперь вставим флэшку - и получим довольно большой текст с множеством параметров, характеризующих произошедшее событие. Затем udevadm monitor снова затихает в ожидании следующего события. Если флэшку вынуть, то это рассматривается как очередное событие, и на экран выдается соответствующий текст. Для завершения работы нажимаем Ctrl-C.
В микрософтовских ОС по факту события просто новому устройству присваивается соответствующая буква-идентификатор, например f: - и на этом, собственно всё: очередное дерево считается посаженным. Открываем устройство с помощью Проводника - и видим находящиеся на нем файлы и каталоги. В наших системах в каталоге /dev создается очередная нода устройства, например sdf (практически по аналогии с DOS/Windows), и символические ссылки на устройство в каталогах /dev/disk/by-id, /dev/disk/by-path и /dev/disk/by-uuid (и, возможно, /dev/disk/by-label). Но это еще не все: открыть устройство и увидеть находящиеся на нем файлы до поры-до времени не удастся, потому что не определены роль и место файлов на флэшке в общем хозяйстве. Чтобы нам получить доступ к находящимся на диске файлам, надо роль и место определить, т. е. «привить» (примонтировать) файловую систему флэшки к строго определенной ветви нашего большого файлового дерева. Для этого даем команду mount, которую я кратко опишу чуть ниже, а сейчас я хочу остановиться на одной особенности юниксоидных ОС, не имеющей аналогов в микрософтовском ПО.

О жестких дисках и разделах

Как мы знаем, устройство типа жесткого диска («винчестера») во всех ОС принято разбивать на разделы («логические диски») и размещать определенные файлы и каталоги на разных разделах (в зависимости, как уже говорилось, от их роли и места в решении задач на конкретном компьютере). В самом простом случае весь винчестер представляет собой один раздел, но все равно раздел есть. В DOS/Windows каждый раздел, если конечно на нем есть файлы и каталоги, рассматривается как отдельное устройство, и ему присваивается своя буква. Например, если на компьютере два винчестера, и на первом три раздела, а на втором два, то получим «логические диски» C:, D:, E:, F: и G:, и не сразу поймешь, где они физически находятся. На картах памяти разделов, как правило, не делают, так что каждая карта памяти представлена одной буквой. В наших ОС винчестеры представлены как файлы (ноды) /dev/sda, /dev/sdb и т. д. - но это относится к винчестеру как изделию. Разделы на каждом винчестере представлены нодами /dev/sda1, /dev/sda2 и т. д. Таким образом, нетрудно понять, на каком физическом винчестере располагается интересующий нас логический раздел. Что же касается устройств типа флэшек, то для одного устройства может создаваться одна нода (например /dev/sdf) или две (/dev/sdf и /dev/sdf1). От чего это зависит, мне пока что непонятно. Могу только сказать, что, если в устройстве имеется стандартная карта памяти, например micro-sd, вставляемая в кард-ридер, то почти наверняка будет иметь место вторая ситуация, однако имеются исключения. В такой ситуации файловая система, подлежащая монтированию, представлена нодой sdf1, аналогично разделам на винчестерах.

Опции монтирования

Это «нюансы» того, как операционная система должна обращаться с файлами и каталогами на каждом конкретном устройстве. Их довольно много, выглядят они несколько путано, и вся эта система опций монтирования носит на себе следы многочисленных кусочно-ямочных улучшизаций. Перечислю некоторые опции, начиная с тех, которыми владею сам.
rw/ro - использовать диск для чтения и записи или только для чтения;
exec/noexec - разрешать/запрещать выполнение программ, если они имеются в данной файловой системе;
sync/async - синхронный/асинхронный режим записи: при синхронном режиме каждый файл записывается немедленно, а при асинхронном файлы накапливаются в буфере (временном хранилище) и затем записываются на диск «оптом», что несколько повышает скорость и продлевает жизнь аппаратуры;
flush=N - используется совместно с async, «временное хранилище» сбрасывается на диск периодически через N секунд;
atime/noatime - обновлять/не обновлять время в каталоге файлов при каждом обращении к файлу;
diratime/nodiratime - аналогично для каталогов;
relatime - обновлять время только при создании или изменении файла;
codepage=M,iocharset=N - нужны для устройств, записанных на других компьютерах, если в именах файлов и в самих файлах присутствуют буквы, отличные от латинских, чтобы дать понять нашему компьютеру, «по-каковски это написано» (для России codepage=866,iocharset=koi8-r);
utf8 - из той же серии: мне ее хватает для работы с флэшками, где могут быть файлы с русскими буквами в именах;
user,users/nouser - кто имеет право монтировать и размонтировать: nouser - только рут, user - монтировать может рядовой пользователь, и размонтировать - только он же, users - монтировать и размонтировать может любой пользователь;
uid=M,gid=N - позволяют назначить владельца и группу для файловой системы, не поддерживающей права доступа (важно для флэшек - на них файловая система FAT);
user_id,group_id - почти то же самое, но в современной нотации;
fmask,dmask,umask - задают права доступа к файлам и каталогам;
dev/nodev - разрешать/запрещать создание на данном носителе файлов-устройств (файлы-устройства - небезопасные игрушки, так что файловые системы, предназначенные для пользовательских данных, монтируются с опцией nodev);
suid/nosuid;
blksize=N;
default_permissions;
allow_other - эта опция используется только при монтировании по сети и дает возможность другим пользователям также получать доступ к данному ресурсу;
shortname=mixed;
unhide

Монтирование файлового дерева при загрузке системы. Файлы /etc/fstab и /etc/mtab

На определенном этапе загрузки линуксоидной системы производится монтирование файлового дерева из частей, располагающихся на разных жестких дисках, подключенных к компьютеру. Поскольку наши системы используются не только на домашних и игровых компьютерах, но и на серверах, к компьютеру может быть подключено множество винчестеров, да еще на каждом из них может быть несколько разделов. Для описания того, как из этого всего следует собирать файловое дерево, предназначен файл /etc/fstab, формирующийся в процессе установки ОС на компьютер. Каждая строка в нем соответствует одному из дисков (разделов), используемых системой. В строке 6 полей, на данном этапе мы рассмотрим первые четыре: нода устройства, точка монтирования, тип файловой системы (или, в более общем плане, в каком виде хранится информация, поскольку она может храниться не только в виде файловой системы, но и как-то иначе) и опции монтирования.
Для примера: как это сделано у меня.
В своих статьях я придерживаюсь определенной системы представления текста. Моноспейсный текст имитирует экран компьютера, на котором мы видим наши команды (подчеркнутым шрифтом) и ответ системы на них.

igor@ibmnote ~ $ cat /etc/fstab
/dev/sda3 / ext4 rw,async,noatime 0 0
/dev/sda2 none swap sw 0 0
/dev/cdrom /mnt/cdrom auto noauto,ro 0 0
/dev/sda1 /media/windows ntfs ro 0 0


У меня это не сервер, а обычный ноутбук с единственным винчестером /dev/sda, с тремя разделами на нем: на разделе /dev/sda1 установлены Windows, этот раздел монтируется как /media/windows (вторая колонка) с опцией «только для чтения» (ro - четвертая колонка), поскольку файловая система на нем ntfs (третья колонка). Раздел /dev/sda2 отведен для подкачки. Он не содержит файловой системы и не монтируется (none во второй колонке, swap в третьей). И, наконец, раздел /dev/sda3 представляет собой корневой каталог, со всеми вложенными каталогами. Здесь почти вся моя система Linux, что вообще-то не совсем хорошо и не совсем правильно. Почему я употребил слово «почти», будет описано чуть ниже. Ro, rw, async, noatime и т. п. в четвертой колонке нам уже знакомы. Noauto - это тоже специфическая опция монтирования, которая пишется только в файле /etc/fstab и показывает, что данная ФС не должна автоматически монтироваться при загрузке ОС (в этом файле могут быть описаны и те ФС, которые монтируются только при необходимости). Пятую и шестую колонки мы сейчас не рассматриваем (они показывают, нуждается ли данная ФС в регулярном резервном копировании и в проверке при загрузке - у меня решено, что не нуждаются).
Устройство /dev/cdrom имеет точку монтирования /mnt/cdrom, тип файловой системы определяется автоматически для каждого конкретного диска (auto в третьей колонке), но монтируется он не при загрузке системы, а только по явной команде пользователя (noauto в четвертой колонке).
Все строки файла, кроме /dev/sda1, сгенерированы автоматически при установке системы, а /dev/sda1 я добавил сам, чтобы иметь возможность передавать файлы из windows в linux (передача в противоположном направлении сопряжена с рядом трудностей). В таком виде файл существует уже достаточно долго.
На последующих этапах загрузки ОС к файловому дереву добавляется целый ряд «псевдофайловых систем», которые не размещаются ни на каких дисках, а просто представляют собой некие информационные массивы, построенные по иерархическому принципу, так что могут быть представлены как файлы и каталоги.
Чтобы увидеть, как смонтировалось файловое дерево по окончании загрузки системы, можно дать такие команды:

igor@ibmnote ~ $ cat /etc/mtab
rootfs / rootfs rw 0 0
803 / ext4 rw,relatime,data=ordered 0 0
devtmpfs /dev devtmpfs rw,relatime,size=255780k,nr_inodes=63945,mode=755 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /run tmpfs rw,nosuid,nodev,relatime,size=51192k,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime 0 0
sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
/dev/sda1 /media/windows fuseblk ro,nosuid,nodev,allow_other,blksize=4096 0 0
igor@ibmnote ~ $ mount
rootfs on / type rootfs (rw)
803 on / type ext4 (rw,relatime,data=ordered)
devtmpfs on /dev type devtmpfs (rw,relatime,size=255780k,nr_inodes=63945,mode=755)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /run type tmpfs (rw,nosuid,nodev,relatime,size=51192k,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
/dev/sda1 on /media/windows type fuseblk (ro,nosuid,nodev,allow_other,blksize=4096)


Как видим, результаты двух команд по существу одинаковы, хотя представлены в несколько разном формате. Команда mount без параметров просто выдает список всего, что смонтировано. Файл /etc/mtab также хранит информацию о смонтированных ФС. Этот файл создается системой, и пользователю изменять его не следует. Заметим, что команда cat отображает настоящее содержимое файла (/etc/mtab - символическая ссылка, которая в Дебиане ведет на /proc/mounts).
Devtmpfs, proc,tmpfs,devpts,shm,sysfs - это как раз псевдофайловые системы.
Вывод этих команд можно сделать более наглядным, если использовать утилиту column, например так:

igor@ibmnote ~ $ mount | column -t

Так выглядит файловое дерево после загрузки ОС. В дальнейшем, в процессе работы пользователя на компьютере, к нему могут добавляться еще ветви, и мы сможем их увидеть, используя вышеописанные команды.
Рекомендуемая статья:
https://losst.ru/avtomaticheskoe-montirovanie-fstab-i-systemd

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

Прежде чем подходить собственно к операции монтирования, изучим несколько вспомогательных команд, которые помогут нам ориентироваться как в нашем файловом хозяйстве, так и в вопросах его обслуживания.
Команды, которые рассматриваются в этой главе, требуют рутовых полномочий.
Файловая система каждого устройства («диска») может иметь метку - имя, которое мы можем задать произвольно в любой момент и затем использовать в повседневной жизни.
Для того чтобы задать метку, в линуксах имеется много утилит: e2label (из пакета e2fsprogs), devlabel, dosfslabel, fatlabel (две последних - для файловых систем FAT, которые применяются на флэшках) - все они в той или иной степени взаимозаменяемы.
Команда findfs позволяет найти устройство по заданной метке.
Команда blkid выдает список блочных устройств («дисков»), подключенных к компьютеру. Для каждого «диска» указывается его уникальный идентификатор (UUID), присваиваемый при создании файловой системы («высокоуровневом форматировании»), и тип файловой системы:

root@bigwhite:/home/igor# blkid
/dev/sda2: UUID=«32ca5387-fd99-4749-b6be-a20bcb63f430» TYPE=«swap»
/dev/sda1: UUID=«8bce861f-9372-45f0-bba6-2341fcc864da» TYPE=«ext4»
/dev/sda3: UUID=«010044d0-e739-4293-bb7e-a5396ab94158» TYPE=«ext4»


(Этот пример - с другого компьютера, так что вывод не «перекликается» с тем, что мы видели в предыдущих примерах)
Обратите внимание на типы ФС. TYPE=«ext4» - наиболее употребительная современная ФС для линуксовых разделов на жестких дисках. TYPE=«swap» - это вообще не файловая система - этот раздел жесткого диска предназначен для «подкачки» (свопинга).
Команда fdisk -l выдает список жестких дисков, подключенных к компьютеру, со всеми имеющимися на них разделами, и некоторые количественные характеристики для каждого диска и раздела:

root@bigwhite:/home/igor# fdisk -l
Disk /dev/sda: 40.0 GB, 40020664320 bytes
255 heads, 63 sectors/track, 4865 cylinders, total 78165360 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xd8443148
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 11718655 5858304 83 Linux
/dev/sda2 11718656 13672447 976896 82 Linux swap / Solaris
/dev/sda3 13672448 78163967 32245760 83 Linux


Внимание, fdisk - потенциально небезопасная игрушка! Перед использованием изучайте мануал! (вообще-то это утилита для разметки разделов на «винчестере»).
Команда df вообще-то придумана для того чтобы увидеть, сколько свободного места у нас на дисках (df - disk free), но кроме того выдает еще кое-какую полезную информацию:

igor@bigwhite:~$ df
Файловая система 1K-блоков Использовано Доступно Использовано% Cмонтировано в
rootfs 5766260 3242632 2230716 60% /
udev 10240 0 10240 0% /dev
tmpfs 100168 1652 98516 2% /run
/dev/disk/by-uuid/8bce…1fcc864da 5766260 3242632 2230716 60% /
tmpfs 5120 0 5120 0% /run/lock
tmpfs 395700 0 395700 0% /run/shm
/dev/sda3 31736808 17473684 12650836 59% /home


Команда lsblk выдает информацию о жестких дисках и разделах. К сожалению, формат вики не позволяет воспроизвести ее вывод - попробуйте сами.
Также много интересной информации можно получить, просматривая каталог /dev/disk и его подкаталоги (с использованием общеупотребительных команд типа cd и ls) - здесь я на этом не буду останавливаться.

Команда монтирования: mount

В самом простом виде команда выглядит так:
mount нода точкамонтирования,
где точкамонтирования - каталог (уже существующий в нашем едином файловом пространстве). Внимание: эта команда требует полномочий суперпользователя.
Пример:

root@ibmnote ~ # mount /dev/sdf /media/transcend

(в ответ тишина, если все в порядке).
При выключении компьютера штатными командами ОС файловая система размонтируется. Отмонтировать конкретное устройство, если оно более не нужно для работы, можно и раньше, для этого имеется команда umount. Для этой команды достаточно указать один аргумент - или ноду, или точку монтирования.
Если устройство съемное (а в настоящей статье мы обсуждаем именно такие), то размонтировать необходимо до того, как флэшка будет вынута из USB-гнезда.
Чаще всего в команде монтирования указывается нода устройства, находящаяся в каталоге /dev. Однако в качестве аргумента команды mount можно использовать не только саму эту ноду, но и символические ссылки на нее (описанные выше). Пример:

mount /dev/disk/by-uuid/E407-1E05 /media/texet

Это были самые простые примеры монтирования без опций (точнее, используются опции «по умолчанию»). В жизни, как правило, не обойтись без указания опций для данной конкретной задачи. Опции задаются списком через запятую, без пробелов, а списку предшествует ключ -o. Пример:

mount -o ro,utf8 /dev/sdb /media/videocamera

Теоретически в команде монтирования следует указывать тип файловой системы, например vfat или ntfs (перед этим поставить ключ -t). Для справки: ntfs используется в Windows, начиная с NT, а в более старых виндах и в DOS использовались ФС семейства fat/vfat. По жизни, однако, в большинстве случаев линукс автоматически распознает тип файловой системы, так что я никогда его не указываю, и ни разу не сталкивался с тем, чтобы из-за этого монтирование не получилось.
Тем не менее, как узнать тип ФС на некотором конкретном устройстве? Командуем:

root@ibmnote ~ # udevadm info -q env -n нодаустройства | grep ID_FS
ID_FS_TYPE=vfat
ID_FS_USAGE=filesystem
ID_FS_UUID=9AFB-1897
ID_FS_UUID_ENC=9AFB-1897
ID_FS_VERSION=FAT32


Udevadm info - вспомогательная утилита, о которой речь впереди. По ключу -q env она выдаст т. н. переменные окружения, соответствующие интересующему нас устройству. Всего переменных окружения порядка 20 штук. Команда grep отберет из них те, имена которых содержат последовательность знаков ID_FS. По ним нетрудно определить тип ФС: в данном примере имеем fat32 из семейства vfat, что закономерно и прогнозируемо для флэшек.
Аналогичные результаты можно получить с помощью udevadm monitor.
См. также man mount.

Постановка задачи автомонтирования

При частом использовании различных устройств со сменными носителями описанная выше операция монтирования может быть довольно хлопотной, и даже может стать источником дополнительных непоняток. Возникает желание как-то эту операцию автоматизировать, тем более что линукс - система, изначально построенная с расчетом на то, чтобы каждый пользователь подгонял что-то под свои потребности.
Мои потребности достаточно скромные. Я не считаю себя любителем гаджетов, но и у меня они имеются в некотором множестве: во-первых, фотоаппарат и автомобильный видеорегистратор. Во-вторых, сотовый телефон, который я порой использую в качестве фотоаппарата, а порой в качестве аудиоплеера. В-третьих, электронная книжка, которую я не столько читаю, сколько использую для просмотра фотографий и воспроизведения музыки. В-четвертых, просто USB-флэшка, кард-ридер и карты памяти к нему… В общем, джентльменский набор гаджетов, каждый из которых время от времени приходится подключать к компьютеру. Соответственно, каждый раз при подключении нужно подавать команду монтирования, и ничего при этом не перепутать. Я хочу, чтобы эта команда подавалась автоматически, и каждый раз с надлежащими параметрами: каждый гаджет должен монтировался в некоторую постоянную точку, специально для него заготовленную (хотя не обязательно строго индивидуальную), снабженную «говорящим» именем:
вся фото-видеотехника –> /media/camera
электронная книжка –> /media/ebook
USB-флэшка Kingston Data Traveller –> /media/datatraveller
кард-ридеры и карты памяти –> монтировать в подкаталоги каталога /media с именами, описывающими типы карт памяти.
Ну и конечно не забыть про опции монтирования: как минимум uid=1000,utf8.
Для решения этой задачи в линуксах предлагается целый ряд механизмов: autofs, udisks… Собственные процедуры полуавтоматического монтирования имеются в файловых менеджерах типа thunar и в графической оболочке xfce, но мне они кажутся не очень удачными: имя точки монтирования получается «птичьим». Я использую udev.
Подход к решению задачи типично юниксоидный: написание конфигурационных файлов с помощью вашего любимого текстового редактора, который у вас, будем надеяться, есть.

Несколько замечаний про udev

Udev - вещь достаточно универсальная, его компетенция простым автомонтированием гаджетов отнюдь не исчерпывается (некоторые даже пишут, что он для этого не предназначен - непонятно почему). Однако по жизни именно автомонтирование - задача номер один, при решении которой люди в первую очередь вспоминают про udev.
Udev независим ни от файл-менеджеров, ни от используемой вами графической оболочки (desktop environment, window manager…), ни вообще от X-сервера, то есть описанная ниже технология применима даже к компьютеру, совсем не имеющему графической периферии.
Второе. В бинарных дистрибутивах, как правило, имеются свои варианты решения поставленной задачи, разработанные для нас (за нас) создателями конкретного дистрибутива и, в отличие от того что сказано чуть выше, «привязанные» к графическим средствам этого дистрибутива. Если вы, подобно вашему покорному слуге, считаете их неудачными и хотите решить задачу по-своему, то надо понимать, что это обходное решение - «костыль». В данной ситуации это слово не несет отрицательной эмоциональной нагрузки, но этот «костыль» может конфликтовать со стандартным решением, которого никто не отменял. У меня, например, тунар при попытке обратиться к флэшке, примонтированной через udev, просто «вылетает» со скоростью, обычно ему не свойственной.

Краткое описание принципа работы udev

Как уже говорилось, в каждой ОС есть механизмы, следящие за событиями и докладывающие о них, и есть механизмы, получающие эти доклады и предпринимающие должные действия по факту событий. К последним относится udev - демон, который характеризуется тем, что может анализировать информацию о событиях согласно правилам, написанным для него пользователем данного конкретного компьютера.
Если вы когда-нибудь в школе или в институте изучали основы советского (российского) государства и права, то вам наверняка рассказывали, как устроена правовая норма: «если имеет место А, то следует обеспечить Б», причем и А и Б могут представлять собой некие множества. В реальных законах я этого не видел, но правила udev строятся именно по такому принципу. То есть каждое правило состоит из опознавательной и исполнительной частей.
Правила udev записываются в одну строчку - перенос строк не предусмотрен. Я, однако, при написании этой статьи вынужден буду к нему прибегать, поскольку правила обычно достаточно длинные.
Вообще-то udev имеет и свои правила, размещающиеся в определенных файлах, но мы туда лезть не будем. Для наших правил отведен каталог /etc/udev/rules.d, и в нем мы можем создавать свои файлы с расширением rules. По неписанной традиции имена файлов следует начинать с двузначного числа. При запуске udev читает эти файлы в алфавитном порядке имен и строит «общее пространство» правил, согласно которым он будет действовать в дальнейшем. Получив сообщение о наступившем событии, udev просматривает правила, и, если обнаруживается соответствие («имеет место А»), то правило выполняется («обеспечивается Б»). На этом udev не останавливается - просматривает все правила. Такое решение продиктовано тем, что не всегда удается обработать событие одним правилом.
Кроме собственно демона, в пакет udev входят утилиты для администрирования:
уже рассмотренная выше программа udevadm monitor позволяет пользователю наглядно наблюдать информацию о происходящих событиях; udevadm info - выдает параметры, которые можно использовать для написания правил;
udevadm control - управление демоном.
Названия, которые я здесь привожу, относятся к дебиану и генте. В других дистрибутивах могут быть названия типа udevinfo.

Правила написания правил

Каждое правило записывается в виде последовательности ключей - опознавательных (фильтров) и исполнительных. Ключи разделяются запятыми или пробелами. Можно ли ставить пробелы внутри ключей? - Боюсь что нет. Каждый ключ представляет собой триаду <наименование><оператор>«значение», где <оператор> - знак или комбинация знаков.
Фильтр по равенству:
наименование==«значение»
Фильтр по неравенству:
наименование!=«значение»
(двойной знак равенства трактуется как сравнение, а восклицательный знак как отрицание, подобно тому как принято в некоторых языках программирования);
Ключи присваивания:
имяпеременной=«значение»
(значение единственное, но не окончательное - может быть переопределено позже);
имяпеременной:=«значение»
(значение единственное и окончательное) - здесь мы снова видим некоторую аналогию с языками программирования;
имяпеременной+=«значение»
(переменная имеет множество значений, и это значение добавляется, не отменяя других);
Ключ создания альтернативного имени устройства (жесткой ссылки):
NAME=«имяустройства»
Ключ запуска специальной программы для выработки альтернативного имени устройства:
PROGRAM=«имяпрограммы»
Этот ключ предназначен именно для запуска программы-«именовальщика устройств» и не для чего-либо другого.
Ключ создания символической ссылки:
SYMLINK+=«имяссылки»
Символическая ссылка создается в дополнение к имеющимся, поэтому знак += необходим. И здесь я считаю необходимым обратить внимание на такую фичу: символическая ссылка может указывать либо на ноду с имеющейся на ней файловой системой, либо на ноду устройства, на котором реально присутствует хотя бы один раздел с файловой системой. То есть, если мы подключаем к компьютеру кард-ридер, в который не вставлена карта памяти, то будут созданы только стандартные ноды для кард-ридера и для карты памяти, а символические ссылки - нет, при этом никакого сообщения об ошибке мы не получим.
Ключ выполнения команды ОС:
RUN+=«команда»
И снова «толстая тонкость». Программы, запускаемые через посредство udev, работают иначе, чем если бы мы запускали их с консоли. Первое: надо всегда указывать полный путь к файлу, не полагаясь на системную переменную PATH. Второе: если программа пытается выдавать какие-то данные на стандартный вывод, то эти данные «уйдут в никуда», на экране мы их не увидим. И перенаправить стандартный вывод куда-либо, используя знаки > или |, тоже не удастся. И стандартный ввод - аналогично. И третье, самое поганое: если программа обнаруживает ошибку, то никакого сообщения на экран мы тоже не получим. По большому счету, для контроля действий udev в реальном времени можно использовать только звуковой вывод.
С поправкой на указанные обстоятельства, в RUN-ключе можно указывать практически любые программы, в т. ч. самостоятельно написанные bash-скрипты (при написании скрипта не забыть сделать его исполняемым с помощью команды chmod). Можно вообще все действия по подготовке монтирования прописать в скрипт, а udev использовать только для запуска этого скрипта.

Информация для написания правил

Выше мы рассмотрели несколько исполнительных ключей, а сейчас более подробно остановимся на опознавательных ключах: какие бывают наименования и значения?
Рассмотрим один из возможных подходов. Для начала даем команду:

root@ibmnote ~ # ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3


Здесь sda - мой винчестер, sda1…sda3 - имеющиеся на нем разделы.
Вставляем устройство, для которого хотим решить задачу автомонтирования, и снова даем ту же самую команду:

root@ibmnote ~ # ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb /dev/sdb1


- видим, что появился новый «диск» sdb и на нем «раздел» sdb1 с файловой системой. Чтобы получить информацию для составления опознавательной части правил udev, даем следующую команду:

root@ibmnote ~ # udevadm info -a -p $(udevadm info -q path -n /dev/sdb)
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host2/target2:0:0/2:0:0:0/block/sdb':
  KERNEL=="sdb"
  SUBSYSTEM=="block"
  DRIVER==""
  ATTR{ro}=="0"
  ATTR{size}=="7835648"
  ATTR{stat}==" 181  172  2684  313  0  0  0  0  0  226  313"
  ATTR{range}=="16"
  ATTR{discard_alignment}=="0"
  ATTR{events}=="media_change"
  ATTR{ext_range}=="256"
  ATTR{events_poll_msecs}=="-1"
  ATTR{alignment_offset}=="0"
  ATTR{inflight}=="       0        0"
  ATTR{removable}=="1"
  ATTR{capability}=="51"
  ATTR{events_async}==""
looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host2/target2:0:0/2:0:0:0':
  KERNELS=="2:0:0:0"
  SUBSYSTEMS=="scsi"
  DRIVERS=="sd"
  ATTRS{rev}=="PMAP"
  ATTRS{type}=="0"
  ATTRS{scsi_level}=="0"
  ATTRS{model}=="DataTraveler 2.0"
  ATTRS{state}=="running"
  ATTRS{queue_type}=="none"
  ATTRS{iodone_cnt}=="0x198"
  ATTRS{iorequest_cnt}=="0x198"
  ATTRS{device_busy}=="0"
  ATTRS{evt_capacity_change_reported}=="0"
  ATTRS{timeout}=="30"
  ATTRS{evt_media_change}=="0"
  ATTRS{max_sectors}=="240"
  ATTRS{ioerr_cnt}=="0x2"
  ATTRS{queue_depth}=="1"
  ATTRS{vendor}=="Kingston"
  ATTRS{evt_soft_threshold_reached}=="0"
  ATTRS{device_blocked}=="0"
  ATTRS{evt_mode_parameter_change_reported}=="0"
  ATTRS{evt_lun_change_reported}=="0"
  ATTRS{evt_inquiry_change_reported}=="0"
  ATTRS{iocounterbits}=="32"
  ATTRS{eh_timeout}=="10"
  <дальше неинтересно>

«Механика» этой команды мне самому не до конца понятна, но это работает. Хотя такой же результат можно получить и командой с несколько другим синтаксисом: udevadm info -a -p /sys/block/sdb. Результат представляет собой довольно большой текст, состоящий из нескольких (порядка 10) блоков, из которых для нас наиболее интересны первые два или три.
Первый блок описывает само интересующее нас устройство («потомок» или «дочернее устройство»), а последующие - «родительские устройства», а по большому счету - более глубокие уровни абстракции, каждому из которых соответствует некоторая процедура в ОС, и эта процедура ждет определенных данных для работы.
Каждый блок содержит имя устройства (KERNEL/KERNELS), присвоенное устройству ядром системы, и набор параметров - атрибутов: для устройства-потомка атрибуты начинаются со слова ATTR, для родительских устройств - со слова ATTRS. Каждый атрибут - практически готовый опознавательный ключ, и двойной знак равенства прозрачно намекает на возможность копипастить атрибуты отсюда прямо в правила.
Здесь нужно еще учесть вот какую тонкость: в одном правиле можно использовать атрибуты «устройства-потомка» и только какого-то одного из «родительских» устройств.
Но так много ключей, как здесь, нам в реальной жизни не нужно. И здесь сразу же совет: используйте только те атрибуты, смысл которых вам понятен. Большая часть атрибутов - бюрократические формальности: разработчики системы ввели их на будущее, а затем сами благополучно про них забыли. Если произвести такую же операцию с несколькими разными устройствами, то станет ясно, что многие атрибуты у разных устройств одинаковы, другие же различаются - вот их-то и надо использовать в качестве ключей опознания.
Обратите внимание на ATTRS{vendor} и ATTRS{model} в родительском устройстве: это фирма-изготовитель и модель устройства - превосходная информация для того, чтобы отличить устройство от множества других. (у других устройств может быть ATTRS{manufacturer}).
Аналогичная команда для sdb1:

root@ibmnote ~ # udevadm info -a -p $(udevadm info -q path -n /dev/sdb1)
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host2/target2:0:0/2:0:0:0/bloc/sd/sdb1':
  KERNEL=="sdb1"
  SUBSYSTEM=="block"
  DRIVER==""
  ATTR{ro}=="0"
  ATTR{size}=="7833600"
  ATTR{stat}==" 136  118  1892  220  0  0  0  0  0  160  220"
  ATTR{partition}=="1"
  ATTR{start}=="2048"
  ATTR{discard_alignment}=="0"
  ATTR{alignment_offset}=="0"
  ATTR{inflight}=="       0        0"
looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host2/target2:0:0/2:0:0:0/block/sdb':
  KERNELS=="sdb"
  SUBSYSTEMS=="block"
  DRIVERS==""
  ATTRS{ro}=="0"
  ATTRS{size}=="7835648"
  ATTRS{stat}==" 181  172  2684  313  0  0  0  0  0  226  313"
  ATTRS{range}=="16"
  ATTRS{discard_alignment}=="0"
  ATTRS{events}=="media_change"
  ATTRS{ext_range}=="256"
  ATTRS{events_poll_msecs}=="-1"
  ATTRS{alignment_offset}=="0"
  ATTRS{inflight}=="       0        0"
  ATTRS{removable}=="1"
  ATTRS{capability}=="51"
  ATTRS{events_async}==""
looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host2/target2:0:0/2:0:0:0':
  KERNELS=="2:0:0:0"
  SUBSYSTEMS=="scsi"
  DRIVERS=="sd"
  ATTRS{rev}=="PMAP"
  ATTRS{type}=="0"
  ATTRS{scsi_level}=="0"
  ATTRS{model}=="DataTraveler 2.0"
  ATTRS{state}=="running"
  ATTRS{queue_type}=="none"
  ATTRS{iodone_cnt}=="0x196"
  ATTRS{iorequest_cnt}=="0x196"
  ATTRS{device_busy}=="0"
  ATTRS{evt_capacity_change_reported}=="0"
  ATTRS{timeout}=="30"
  ATTRS{evt_media_change}=="0"
  ATTRS{max_sectors}=="240"
  ATTRS{ioerr_cnt}=="0x2"
  ATTRS{queue_depth}=="1"
  ATTRS{vendor}=="Kingston"
  ATTRS{evt_soft_threshold_reached}=="0"
  ATTRS{device_blocked}=="0"
  ATTRS{evt_mode_parameter_change_reported}=="0"
  ATTRS{evt_lun_change_reported}=="0"
  ATTRS{evt_inquiry_change_reported}=="0"
  ATTRS{iocounterbits}=="32"
  ATTRS{eh_timeout}=="10"
  <дальше неинтересно>

Сейчас обратите внимание на то, что sdb является родительским устройством по отношению к sdb1, и второй блок атрибутов sdb1 соответствует первому блоку из вывода предыдущей команды, за исключением того что здесь пишут ATTRS вместо ATTR.
Теперь обратите внимание на ATTR{removable}==«1» для sdb и ATTR{partition}==«1» для sdb1. ATTR{partition}==«1» однозначно говорит о том, что это устройство представляет собой «раздел» - следовательно, файлы, подлежащие монтированию, находятся именно здесь, и в команде mount мы должны вписать sdb1. ATTR{removable}==«1» при отсутствии sdb1 ничего бы не означало, но в нашей ситуации sdb1 имеется, и ATTR{removable}==«1» говорит о том, что sdb следует рассматривать как кард-ридер: файлов на нем нет, и монтированию оно не подлежит.

Написание правил

Итак, имеем почти все, что нужно для написания правил… Только не забыть о том, на что я уже указывал выше: правила относятся не к устройству, а к событию, так что нам нужно указать, какого рода событие мы обрабатываем (уточнить можно, исследуя вывод команды udevadm monitor):
ACTION==«add» - добавление устройства
ACTION==«remove» - удаление устройства
ACTION==«change» - смена носителя (для CD/DVD дисков).
В опознавательных ключах (фильтрах) можно использовать символы * и ? аналогично тому, как мы их используем для имен файлов в консольных командах.
В RUN-ключе можно использовать символьную подстановку: знак процент вместе с некоторыми следующими за ним знаками заменяется значением определенной переменной.
%k - имя устройства (sdb1 и т.п.),
%r - каталог устройств (обычно /dev),
%p - полный путь к файлу-устройству (ноде),
%n - цифра в имени устройства,
%E{имяпеременной} - подставить значение переменной окружения (об этом разговор пойдет ниже).
Теперь можем написать правило (как обычно в своих статьях, текст, подлежащий вставке в файл, выделяю курсивом).

ACTION==«add», ATTR{partition}==«1», ATTRS{manufacturer}==«Kingston», RUN+=«/bin/mount -o uid=1000,utf8 /dev/%k /media/datatraveller»

С первым тремя ключами все, надеюсь, ясно. К четвертому: /bin/mount - как уже говорилось, следует указывать полный путь.
/dev/%k = %r/%k = %p - что лучше, люди спорят, я спорить не буду.
Записываем правило в файл, допустим /etc/udev/rules.d/20-usbflash.rules. Теперь, поскольку udev читает правила только при запуске, а в процессе работы не следит за файлами правил, нужно явным образом «ознакомить» его с новым правилом (команда от рута):

root@ibmnote ~ # udevadm control –reload-rules

Утилита udevadm control имеет ряд функций, мне же по жизни нужна только одна эта.
Убеждаемся в том, что каталог /media/datatraveller, назначенный в качестве точки монтирования, существует. Вынимаем флэшку, выжидаем секунд 10 и вставляем обратно - вот сейчас она должна примонтироваться. Проверяем:

root@ibmnote ~ # cat /etc/mtab
rootfs / rootfs rw 0 0
803 / ext4 rw,relatime,data=ordered 0 0
devtmpfs /dev devtmpfs rw,relatime,size=255780k,nr_inodes=63945,mode=755 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /run tmpfs rw,nosuid,nodev,relatime,size=51192k,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime 0 0
sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
/dev/sda1 /media/windows fuseblk ro,nosuid,nodev,allow_other,blksize=4096 0 0
/dev/sdb1 /media/datatraveller vfat rw,uid=1000,utf8 0 0


- заходим в каталог /media/datatraveller и видим файлы и каталоги, находящиеся на флэшке.

Как не следует писать правила

Следующий пример правил udev взят вот отсюда: http://zenux.ru/articles/40/ Рассмотрим, что здесь правильно и что нет.
SUBSYSTEM==«block», KERNEL==«sd[c-z][0-9]», ACTION==«add», RUN+=«/bin/mkdir -p /mnt/flash»
SUBSYSTEM==«block», KERNEL==«sd[c-z][0-9]», ACTION==«add», RUN+=«/bin/mount -O uid=1000 /dev/%k /mnt/flash/»
SUBSYSTEM==«block», KERNEL==«sd[c-z][0-9]», ACTION==«remove», RUN+=«/bin/umount /mnt/flash»
SUBSYSTEM==«block», KERNEL==«sd[c-z][0-9]», ACTION==«remove», RUN+=«/bin/rm -r /mnt/flash»
Опознавательная часть в этих правилах, хотя и написана иначе, чем у меня, сомнений не вызывает. (все уже поняли, что значит «sd[c-z][0-9]»?) Но вот вопрос: зачем для одного и того же события писать несколько правил с одними и теми же фильтрами, когда можно в одно правило вставить два RUN-ключа? Впрочем, это не главное. Наибольшие сомнения вызывает команда размонтирования, выполняемая по факту события ACTION==«remove». Такое событие регистрируется, когда флэшка уже вынута из USB-гнезда, а размонтирование следовало выполнить раньше. Даже если использовать опцию монтирования sync, кто гарантирует, что команда umount не будет нужна? А поскольку компьютер не может предсказать извлечение флэшки, подавать команду umount нужно обязательно вручную перед тем как вынимать флэшку.
Что касается команд mkdir и rm, то все авторы интернет-блогов и статей поступают так, на самом же деле необходимости в этом нет. У меня все каталоги, которые будут служить точками монтирования, созданы заранее и существуют постоянно.

Нестандартные имена для устройств

Как я уже отмечал, помимо собственно автомонтирования, udev может решать множество других задач. В частности, он позволяет «подготовить почву» для ручного монтирования, на случай, если автомонтирование по какой-либо причине не нужно. Например, можно в дополнение к безликим и путаным именам типа sdb1, sdc1… разместить в каталоге /dev альтернативные файлы-ноды с «говорящими» именами, с тем чтобы затем дать команду, например, так:

mount /dev/nokia /media/nokia

Согласитесь, это удобнее, чем mount /dev/sde1 /media/nokia.
Для того чтобы это сделать, udev предлагает несколько путей. Можно создать альтернативное имя устройства («жесткую ссылку») или символическую ссылку. Альтернативное имя можно прописать непосредственно в правиле или использовать для его выработки специальную программу. Соответствующие исполнительные ключи в правиле будут выглядеть так:
NAME=«nokia»
или
PROGRAM=«nokiadevicerenamer»
(подразумевается, что программа с таким именем существует и выдает нечто, что udev использует в качестве имени ноды устройства)
или
SYMLINK+=«nokia»
Какой вариант предпочесть? Сколько людей, столько мнений. Излагаю свое, личное и потому неизбежно субъективное.
Каталог /dev содержит десятки устройств. В этом списке более-менее легко найти sda, sdb, sdc и т. п., поскольку они расположены компактной группой. А вот альтернативные имена в этом списке даже чисто зрительно практически теряются, да еще как их сопоставить с соответствующими стандартными именами? Если же использовать символические ссылки, то, во-первых, символическая ссылка легко отличаются от простых файлов даже по «внешнему виду» в выводе команды ls -l. Во-вторых, сразу видно, на какую стандартную ноду она ссылается. Если подключено несколько устройств, то нетрудно определить, на какую стандартную ноду «село» каждое из них. Поэтому я предпочитаю этот вариант.

Использование переменных окружения

Кроме атрибутов устройств, каждому событию ставится в соответствие еще одно «досье», называемое английским словом environment. Это слово с некоторой приблизительностью переводят как среда или окружение, но толку от такого перевода мало, потому что к пониманию сути он не приближает.
На самом деле окружение - используемый во многих близких к UNIX системах (в том числе в ДОС) инструмент для изменения «поведения» программ путем передачи им некоторых параметров. Иначе говоря, любая программа «работает в своей среде» (в своем окружении), а формируется эта среда процессом, вызывающим эту программу (родительским).
В нашем случае родительский процесс - UDEV, и он передает блок переменных окружения дочернему процессу, вызываемому им. В частности, если правило содержит RUN-секцию, то программа, вызываемая согласно этому правилу, будет работать именно в этом окружении. Увидеть окружение можно командой:

root@ibmnote ~ # udevadm info -q env -n /dev/sdb1
DEVLINKS=/dev/card /dev/disk/by-id/usb-Kingston_DataTraveler_2.0_001372982F25EA61C0000000-0:0-part1
/dev/disk/by-path/pci-0000:00:1d.7-usb-0:3:1.0-scsi-0:0:0:0-part1
/dev/disk/by-uuid/9AFB-1897 /dev/kingston
DEVNAME=/dev/sdc1
DEVPATH=/devices/pci0000:00/0000:00:1d.7/usb1/1-3/1-3:1.0/host4/target4:0:0/4:0:0:0/block/sdc/sdc1
DEVTYPE=partition
ID_BUS=usb
ID_FS_TYPE=vfat
ID_FS_USAGE=filesystem
ID_FS_UUID=9AFB-1897
ID_FS_UUID_ENC=9AFB-1897
ID_FS_LABEL=portfel
ID_FS_VERSION=FAT32
ID_INSTANCE=0:0
ID_MODEL=DataTraveler_2.0
ID_MODEL_ENC=DataTraveler\x202.0
ID_MODEL_ID=1603
ID_PART_ENTRY_DISK=8:32
ID_PART_ENTRY_NUMBER=1
ID_PART_ENTRY_OFFSET=2048
ID_PART_ENTRY_SCHEME=dos
ID_PART_ENTRY_SIZE=7833600
ID_PART_ENTRY_TYPE=0xc
ID_PART_ENTRY_UUID=c3072e18-01
ID_PART_TABLE_TYPE=dos
ID_PART_TABLE_UUID=c3072e18
ID_PATH=pci-0000:00:1d.7-usb-0:3:1.0-scsi-0:0:0:0
ID_PATH_TAG=pci-0000_00_1d_7-usb-0_3_1_0-scsi-0_0_0_0
ID_REVISION=PMAP
ID_SERIAL=Kingston_DataTraveler_2.0_001372982F25EA61C0000000-0:0
ID_SERIAL_SHORT=001372982F25EA61C0000000
ID_TYPE=disk
ID_USB_DRIVER=usb-storage
ID_USB_INTERFACES=:080650:
ID_USB_INTERFACE_NUM=00
ID_VENDOR=Kingston
ID_VENDOR_ENC=Kingston
ID_VENDOR_ID=0951
MAJOR=8
MINOR=33
SUBSYSTEM=Block
USEC_INITIALIZED=89510


Такую же информацию, в принципе, выдает и udevadm monitor.
Подобно атрибутам, окружение представляет собой набор переменных, каждая из которых имеет имя и значение. И, опять же подобно атрибутам, большая часть из них нам мало что говорит. Однако обратите внимание на ID_FS_UUID и ID_FS_LABEL. Первая из них - уникальный идентификатор файловой системы (задается автоматически при форматировании), вторая - в досовской терминологии «метка диска» - необязательный параметр, который мы можем задавать по своему усмотрению, используя команду dosfslabel, fatlabel или подобную. Эти переменные позволяют различить даже абсолютно однотипные флэшки (представьте, что их у вас много, а по внешнему виду все одинаковы… на микро-SD-шках даже надписей никаких не сделать). Флэшка, которую мы рассматриваем здесь как пример, имеет еще серийный номер (ID_SERIAL и ID_SERIAL_SHORT), который задается раз и навсегда при изготовлении флэшки, но он есть не у всех флэшек.
В правилах UDEV можно задавать и свои переменные окружения, используя секции присваивания со знаками = , += , := , как описано выше. Переменные окружения можно подставлять в команды, задаваемые RUN-ключами правил, например:

RUN+=«/bin/mount /dev/%k /media/card-%E{ID_FS_LABEL}-%E{ID_FS_UUID}»

Эта команда монтирует флэшку в каталог с хитрым именем, включающим слово card, метку диска и UUID (в предположении, что такой каталог кто-то когда-то уже создал).
В интернете пишут, что такая же подстановка возможна в SYMLINK-ключе - у меня это не работает. Также не получается строить ключи опознания с использованием переменных окружения.
Впрочем, основной способ использования переменных окружения несколько иной: их можно читать прямо из программы, которая будет в этом окружении работать. Мы говорим «программа», подразумеваем «bash-скрипт». Вот пример такого скрипта.
Этот скрипт делает почти то же, что и только что описанная команда, но более интеллигентно. Во-первых, если файловая система имеет метку, то имя точки монтирования будет содержать только ее, а UUID в этом случае не нужен, он только путает. Во-вторых, если каталога с нужным именем нет, то он создается.

#!/bin/bash
# 1). Есть ли у нашей файловой системы метка? (UUID есть всегда)
if test $ID_FS_LABEL
# если есть, то имя точки монтирования будет: card-меткафайловойсистемы
then
ourmountpointname=card-$ID_FS_LABEL
# если метки нет, то имя точки монтирования будет: card-UUID
else
ourmountpointname=card-$ID_FS_UUID
fi
# 2). Существует ли такой каталог? Если нет, то создаем его.
if ! test -d /media/$ourmountpointname
then
mkdir /media/$ourmountpointname
fi
# 3). Монтируем
mount -o utf8,uid=1000 $DEVNAME /media/$ourmountpointname


То есть мы можем обращаться к переменным окружения точно так же, как вообще к переменным в языке командной оболочки (а какие, собственно, еще бывают переменные?)
Назовем этот скрипт, допустим, bashmount. Не забудем вписать первой строкой #!/bin/bash, чтобы система понимала, какую программу использовать для отработки скрипта, и сделать скрипт исполняемым с помощью команды chmod a+x bashmount.

Права доступа

В юниксоидных ОС используются файловые системы семейства ext2/ext3/ext4, требующие задания для каждого файла или каталога тех или иных ограничений по доступу для разных пользователей. Однако мы сейчас говорим о всевозможных гаджетах, а на них обычно имеется файловая система семейства FAT (такая же, как в DOS/Windows). Эта файловая система не поддерживает прав доступа. Поэтому, если монтировать флэшку без специфических опций, то владельцем файловой системы будет root и, соответственно, запись файлов на флэшку будет возможна лишь через su или sudo.
Хорошо это или плохо - решать вам. Я, например, считаю это правильным для тех гаджетов, которые сами пишут на флэшку (пример - фотоаппарат). Но если мы говорим об электронной книжке, то вся запись на ее карту памяти выполняется только пользователем, причем системных файлов, которые следовало бы беречь от непродуманных действий пользователя, там нет. В этой ситуации было бы более логично разрешить запись на флэшку рядовому пользователю. Для этого можно использовать опции монтирования uid, gid, fmask, dmask и umask (рассмотрены выше). В опциях монтирования следует ставить не имена пользователей, а числовые идентификаторы. Рядовой пользователь персонального компьютера обычно имеет uid=1000 (можно уточнить, используя команды id -u или who).
В опциях fmask, dmask и umask ставится восьмеричный код доступа, инверсный по отношению к тому, который ставится в команде chmod. То есть, по сути дела, указываем, что кому запрещено делать, в противоположность тому, как в команде chmod указываем, что кому разрешено. Например, dmask=000,fmask=111 - дают права доступа к каталогам 777, к файлам 666 (напоминаю: для того чтобы войти в каталог, нужно иметь права на его «исполнение».)
Почему так сделано - непонятно.

Немного высшего пилотажа

Правила udev представляют собой специфическую программу. И, как в обычной программе, здесь можно использовать метки и условные переходы. Специального оператора типа IF здесь нет, да он и не очень нужен, поскольку сами правила есть не что иное как условные операторы, хотя и в несколько нетрадиционной записи. Все остальное записывается в том же синтаксисе, что и правила. Метка:
LABEL=«имяметки»
Переход на метку:
GOTO=«имяметки»
Как это сделано у меня:

ACTION!=«add», GOTO=«nogagdet»
KERNEL!=«sd*», GOTO=«nogadget»
#здесь все правила для гаджетов
LABEL=«nogadget»


(обратите внимание на знаки неравенства).
Такая запись сокращает правила, а значит делает их более читаемыми, да и скорость обработки событий несколько возрастает.
Еще один прием, позволяющий изменить последовательность действий udev по обработке события:
OPTIONS+=«last_rule»
Как уже отмечалось выше, при наступлении очередного события udev обычно просматривает все правила, загруженные при запуске. В ряде случаев бывает нужно, выполнив некоторое правило, остановиться на этом, так что все последующие правила не нужны. Для этого в правило добавляется такая опция (не путать с опциями монтирования).
———————
Игорь Романов

igor/udev_i_ego_primenenie_dlja_montirovanija_fajlovoj_sistemy.txt · Последнее изменение: 2019/12/09 23:54 — igor