Bug 39385

Summary: sfdisk не переносит таблицу разделов установленной штатным образом ОС
Product: Sisyphus Reporter: Белаш Константин <bk>
Component: libevmsAssignee: Slava Aseev <ptrnine>
Status: CLOSED FIXED QA Contact: qa-sisyphus
Severity: normal    
Priority: P5 CC: klark, legion, mcpain, mike, rider, zerg
Version: unstable   
Hardware: x86_64   
OS: Linux   

Description Белаш Константин 2020-12-06 12:34:31 MSK
Воспроизводится при установке с alt-workstation-9.1-x86_64.iso.
При инсталляции, на шаге 4/12 установки, выбрал "Использовать неразмеченное пространство", эта опция по-умолчанию.

После установки:
host-79 ~ # lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0   30G  0 disk
├─sda1   8:1    0  255M  0 part /boot/efi
├─sda2   8:2    0  128M  0 part [SWAP]
└─sda3   8:3    0 29.6G  0 part /
sr0     11:0    1 1024M  0 rom
sr1     11:1    1  4.7G  0 rom  /run/media/user/ALT Workstation 9.1 x86_64
host-79 ~ # sfdisk -d /dev/sda > sda.sfdisk
host-79 ~ # cat sda.sfdisk
label: gpt
label-id: E6CE83F9-7327-1341-80CD-EC005EBC8BF1
device: /dev/sda
unit: sectors
first-lba: 32
last-lba: 62914528
table-length: 120

/dev/sda1 : start=        2048, size=      522240, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=E7687235-280B-D747-A66F-87CBFB8B6BD1
/dev/sda2 : start=      524288, size=      262144, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=D4CF5C91-E654-5B44-AEE6-9444A491907F
/dev/sda3 : start=      786432, size=    62126080, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=899A97C0-E0C6-4244-AC1B-3BF3D19710E2
host-79 ~ #


Добавляю диск и переношу разметку:
host-79 ~ # lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0   30G  0 disk
├─sda1   8:1    0  255M  0 part /boot/efi
├─sda2   8:2    0  128M  0 part [SWAP]
└─sda3   8:3    0 29.6G  0 part /
sdb      8:16   0   32G  0 disk
sr0     11:0    1 1024M  0 rom
sr1     11:1    1  4.7G  0 rom  /run/media/user/ALT Workstation 9.1 x86_64
host-79 ~ # sfdisk /dev/sdb < sda.sfdisk
Checking that no-one is using this disk right now ... OK

Disk /dev/sdb: 32 GiB, 34359738368 bytes, 67108864 sectors
Disk model: QEMU HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

>>> Script header accepted.
>>> Script header accepted.
>>> Script header accepted.
>>> Script header accepted.
>>> Script header accepted.
>>> Script header accepted.
>>> Script header accepted.
>>> First LBA specified by script is out of range.
First LBA specified by script is out of range.
Failed to apply script headers, disk label not created.
Leaving.

Если в файле дампа (sda.sfdisk) поменять значения:
first-lba: 32  на  first-lba: 34  и  table-length: 120  на  table-length: 128
то sfdisk отрабатывает нормально.
Comment 1 Leonid Krivoshein 2020-12-06 13:29:13 MSK
Возник ещё один вопрос, прямо не связанный с этим багом к legion@:

Судя по коду sfdisk (util-linux/libfdisk/src/gpt.c#612), данное предупреждение через несколько возвратов -ERANGE превращается в необрабатываему никем фатальную ошибку в случае, если посчитанное значение отличается от умолчального.

Получается, что спецификация GUID/GPT допускает значения, отличные от дефолта, но такую разметку не сможет перетащить sfdisk. Алексей, как думаешь, авторы просто забыли обработать -ERANGE или же это верный ход превращать warnx() в фатальную ошибку?
Comment 2 Leonid Krivoshein 2020-12-06 14:06:10 MSK
(In reply to Белаш Константин from comment #0)
> Если в файле дампа (sda.sfdisk) поменять значения:
> first-lba: 32  на  first-lba: 34  и  table-length: 120  на  table-length: 128
> то sfdisk отрабатывает нормально.

Константин, а можешь показать hexdump второго сектора исходного диска или приложить к багу в бинарном виде? Чтобы знать наверняка, что ошибка в инсталляторе, а не в sfdisk.
Comment 3 Alexey Gladkov 2020-12-06 15:41:04 MSK
(Ответ для Leonid Krivoshein на комментарий #1)
> Возник ещё один вопрос, прямо не связанный с этим багом к legion@:
> 
> Судя по коду sfdisk (util-linux/libfdisk/src/gpt.c#612), данное
> предупреждение через несколько возвратов -ERANGE превращается в
> необрабатываему никем фатальную ошибку в случае, если посчитанное значение
> отличается от умолчального.

Насколько я вижу умолчание это минимальное и максимальное значение. А -ERANGE для first возвращается если он за границами минимального и максимального значений.

> Получается, что спецификация GUID/GPT допускает значения, отличные от
> дефолта, но такую разметку не сможет перетащить sfdisk.

Нет. Тебе нужно, чтобы first-lba и last-lba были в пределах:

esz = sizeof(struct gpt_entry) * GPT_NPARTITIONS / cxt->sector_size;
llba = cxt->total_sectors - 2ULL - esz;
flba = esz + 2ULL; 

> Алексей, как
> думаешь, авторы просто забыли обработать -ERANGE или же это верный ход
> превращать warnx() в фатальную ошибку?

Я не очень понял вопроса. -ERANGE не обрабатывается только в gpt_reset_alignment и в этом случае first и last будут нулями, что скорее всего приведёт к тому, что cxt->first_lba и cxt->last_lba не будут изменены.

В gpt_mknew_header же выход за границы фатален поскольку в хэдере first_usable_lba и last_usable_lba будут нулями оба или же только один из них.
Comment 4 Leonid Krivoshein 2020-12-06 17:57:16 MSK
(In reply to Alexey Gladkov from comment #3)
> (Ответ для Leonid Krivoshein на комментарий #1)
> > Получается, что спецификация GUID/GPT допускает значения, отличные от
> > дефолта, но такую разметку не сможет перетащить sfdisk.
> 
> Нет. Тебе нужно, чтобы first-lba и last-lba были в пределах:
$ git grep -A2 -B1 'First LBA specified by script is out of range' |cut -c9-
if (rc == 0 && (*first < flba || *first > llba)) {
	fdisk_warnx(cxt, _("First LBA specified by script is out of range."));
	return -ERANGE;
}

В данном случае *first = 32, а не 34, значение должно быть в пределах минимума и максимума, определённого умолчанием. То есть, если (32 < 34 || ...), по логике libfdisk означает ошибку.

> > Алексей, как
> > думаешь, авторы просто забыли обработать -ERANGE или же это верный ход
> > превращать warnx() в фатальную ошибку?
> 
> Я не очень понял вопроса. -ERANGE не обрабатывается только в
> gpt_reset_alignment и в этом случае first и last будут нулями
Вроде бы такой здесь получается стек обработки:

libfdisk/src/gpt.c#613 count_first_last_lba()
libfdisk/src/gpt.c#675 gpt_mknew_header()
libfdisk/src/gpt.c#2457 gpt_create_disklabel()
libfdisk/src/script.c#1751 fdisk_create_disklabel()
libfdisk/src/script.c#1546 fdisk_apply_script_headers()
disk-utils/sfdisk#1855 , #1895, #1912.

Отсюда и мой вопрос. Возможно я неправ в том, что спецификация допускает и меньшее значение, надо это проверить.
Comment 5 Leonid Krivoshein 2020-12-06 18:10:06 MSK
(In reply to Leonid Krivoshein from comment #4)
> Возможно я неправ в том, что спецификация допускает и
> меньшее значение, надо это проверить.
The UEFI specification stipulates that a minimum of 16,384 bytes, regardless of sector size, are allocated for the Partition Entry Array. Thus, on a disk with 512-byte sectors, at least 32 sectors are used for the Partition Entry Array, and the first usable block is LBA 34 or higher. While on a 4,096-byte sectors disk, at least 4 sectors are used for the Partition Entry Array, and the first usable block is LBA 6 or higher.

То есть, first_lba действительно должен быть >= 34, но для дисков 4K >= 6.
Comment 6 Leonid Krivoshein 2020-12-06 18:16:44 MSK
Алексей, здесь нет ошибки в sfdisk, выше я на свой вопрос сам же и ответил. Для дисков 4K учитывается sector_size, так что sfdisk правильно делает, что не даёт восстановить разметку с first_lba = 32. Осталось понять, кто создаёт такой кривой GPT-заголовок.
Comment 7 Белаш Константин 2020-12-07 11:00:18 MSK
(Ответ для Leonid Krivoshein на комментарий #2)
> (In reply to Белаш Константин from comment #0)
> > Если в файле дампа (sda.sfdisk) поменять значения:
> > first-lba: 32  на  first-lba: 34  и  table-length: 120  на  table-length: 128
> > то sfdisk отрабатывает нормально.
> 
> Константин, а можешь показать hexdump второго сектора исходного диска или
> приложить к багу в бинарном виде? Чтобы знать наверняка, что ошибка в
> инсталляторе, а не в sfdisk.

# dd if=/dev/sda bs=512 count=2 | hexdump -C
2+0 записей получено
2+0 записей отправлено
1024 байт (1,0 kB, 1,0 KiB) скопирован, 0,000133948 s, 7,6 MB/s
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001c0  02 00 ee 3f e0 ff 01 00  00 00 ff ff bf 03 00 00  |...?............|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 80 00  |................|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200  45 46 49 20 50 41 52 54  00 00 01 00 5c 00 00 00  |EFI PART....\...|
00000210  9d fb 8f f8 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000220  ff ff bf 03 00 00 00 00  20 00 00 00 00 00 00 00  |........ .......|
00000230  e0 ff bf 03 00 00 00 00  f9 83 ce e6 27 73 41 13  |............'sA.|
00000240  80 cd ec 00 5e bc 8b f1  02 00 00 00 00 00 00 00  |....^...........|
00000250  78 00 00 00 80 00 00 00  a3 88 a8 46 00 00 00 00  |x..........F....|
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000400

Это нужно?
Comment 8 Leonid Krivoshein 2020-12-07 22:14:22 MSK
(In reply to Белаш Константин from comment #7)
> Это нужно?
Да, оно. Видно, что разметка испорчена инсталлятором:

> 00000220  ff ff bf 03 00 00 00 00  20 00 00 00 00 00 00 00
Здесь "20 00 00 00 00 00 00 00" -- это и есть first_lba=32, а д.б. 34.
Comment 9 Slava Aseev 2020-12-28 14:50:49 MSK
Нашел вроде бы то самое.
В plugins/gpt/gptsegmgr.c есть вот такая штука:

pt_lba1 = 2;
pt_size = ld->geometry.sectors_per_track - 2;
pt_entry_size = sizeof(gpt_partition); 
pt_count = (pt_size*EVMS_VSECTOR_SIZE) / pt_entry_size;

// Locate the start of useable area immediately after the partition table
start_useable = pt_lba1 + pt_size;


start_useable это как раз first-lba, а pt_count - table-length
если pt_size равен 30, то start_useable будет 32
sizeof(gpt_partition) равен 128, значит pt_count в данном случае равен 120 (30 * 512 / 128)

Выходит, с вот этой двойкой какая-то фигня:
pt_size = ld->geometry.sectors_per_track - 2;

Если ее убрать, то должно получиться start_useable 34 и pt_count 128
Comment 10 Leonid Krivoshein 2020-12-28 15:52:53 MSK
(In reply to Slava Aseev from comment #9)
> Нашел вроде бы то самое.
> В plugins/gpt/gptsegmgr.c есть вот такая штука:
> pt_size = ld->geometry.sectors_per_track - 2;
Тут только один момент. Строка датируется 2006 годом и пришла в Альт с первой сборкой evms-2.5.5-alt2. Текущая версия сборки alt47. А поломка возникла недавно, уже на p9. На p8 там всё правильно проставлялось. Вот пример с моей машины:

label: gpt
label-id: <removed>
device: /dev/nvme0n1
unit: sectors
first-lba: 34
last-lba: 488397134
Comment 11 Slava Aseev 2020-12-28 16:48:12 MSK
(Ответ для Leonid Krivoshein на комментарий #10)
> (In reply to Slava Aseev from comment #9)
> > Нашел вроде бы то самое.
> > В plugins/gpt/gptsegmgr.c есть вот такая штука:
> > pt_size = ld->geometry.sectors_per_track - 2;
> Тут только один момент. Строка датируется 2006 годом и пришла в Альт с
> первой сборкой evms-2.5.5-alt2. Текущая версия сборки alt47. А поломка
> возникла недавно, уже на p9. На p8 там всё правильно проставлялось. Вот
> пример с моей машины:
> 
> label: gpt
> label-id: <removed>
> device: /dev/nvme0n1
> unit: sectors
> first-lba: 34
> last-lba: 488397134

У меня на диске, который я размечал средствами инсталлятора год назад, вот такие цифры:
label: gpt
label-id: C6E76728-F50E-4A4A-A53F-7A38A5469290
device: /dev/nvme0n1
unit: sectors
first-lba: 32
last-lba: 250069648
table-length: 120

Проверил на виртуалке с первым попавшимся образом c7 (evms-2.5.5-alt30) там то же самое:
label: gpt
label-id: 547D4CF0-8887-6543-82F3-374B96872F1C
device: /dev/sda
unit: sectors
first-lba: 32
last-lba: 83886048
table-length: 120
Comment 12 Leonid Krivoshein 2020-12-28 17:10:21 MSK
Возможно, я делал разметку не инсталлятором, а обычными средствами. По крайней мере, раньше эта ошибка не встречалась.

В заголовке GPT firts_lba определено как uint64_t, в evms это lba_t, а то, из чего он считает по этой формуле -- sector_count_t pt_size, причём ld->geometry опциональный член структуры, судя по описанию LOGICALDISK. Сложно догадаться, что находится в ld->geometry.sectors_per_track, т.к. GPT/GUID не рассчитана на C/H/S коррдинацию, а ориентирована на lba. Возникает вопрос, почему там изначально была эта -2 в формуле.
Comment 13 Slava Aseev 2020-12-30 06:36:23 MSK
Тут какая-то совсем жесть происходит.
Пробую на маленьком 10 мегабайтном диске делать GPT средствами evms.

Создал на чистом диске:

label: gpt
label-id: 6E8EE2E2-6BE4-EE4D-B5AA-39D0B7D40502
device: /dev/sda
unit: sectors
first-lba: 23
last-lba: 20951
table-length: 84

Потом пересоздал:

label: gpt
label-id: 7B6A60BB-17EC-CE45-819D-3D10EF73FDD8
device: /dev/sda
unit: sectors
first-lba: 18
last-lba: 20956
table-length: 64

Потом создал с помощью fdisk и пересоздал опять средствами evms:

label: gpt
label-id: 40CDBE9D-AF1D-E44F-B449-4417095CC278
device: /dev/sda
unit: sectors
first-lba: 65
last-lba: 20909
table-length: 252

И затем пересоздал еще раз:

label: gpt
label-id: 809BED2C-E345-B046-8AB2-A6F9325FC246
device: /dev/sda
unit: sectors
first-lba: 60
last-lba: 20914
table-length: 232

Т.е. это все дело либо зависит от исходного состояния диска, либо в ld->geometry.sectors_per_track оказывается вообще что попало.
Поэтому просто убрать двойку оттуда явно недостаточно.
Comment 14 Slava Aseev 2020-12-30 06:53:14 MSK
(Ответ для Leonid Krivoshein на комментарий #12)
> Возможно, я делал разметку не инсталлятором, а обычными средствами. По
> крайней мере, раньше эта ошибка не встречалась.

Т.е. вполне возможно, что вы также размечали инсталлятором. Просто карты сложились чуть по-другому.
Comment 16 Leonid Krivoshein 2020-12-30 14:26:47 MSK
(In reply to Slava Aseev from comment #15)
> Я еще чуть потестирую и, если нет возражений, отправлю.
Константа GPT_NPARTITIONS в исходниках не определена, Вы задали её в заголовочном файле? Константа EVMS_VSECTOR_SIZE определена лишь в одном месте как 512 байт, а это значит, что проект вряд ли поддерживает настоящие "Blue 4Kn" диски и в структуру ld.geometry может приезжать совсем неправильное значение bytes_per_sector. Собственно, варианта всего два: 512 и 4096 и оба лежат в /sys/dev/block/$major:$minor/queue/logical_block_size либо его можно получить через ioctl() как в $(blockdev --getss /dev/$NAME).

Вы правильно делаете, что отвязываете формулу от данных в ld.geometry, но это нужно делать до конца либо иметь понимание, что в неё попадают правильные данные. Вообще правильная формула должна исходить из спецификации, тогда всё просто -- pt_size=16384 скорее константа и она же возможный минимум. При делении этого числа на bytes_per_sector всегда получим либо 32 (для 512-байтных секторов), либо 4 (для "Blue 4Kn" дисков). А значит и first_lba всегда зависит по сути от единственного флага blue4kn.

Но я боюсь, что тестирование на дисках 4K покажет несовместимость libevms и по другим структурам. Актуализировать же код начала 2000-х придётся долго и мучительно. :-)
Comment 17 Slava Aseev 2020-12-30 15:29:45 MSK
(Ответ для Leonid Krivoshein на комментарий #16)
> (In reply to Slava Aseev from comment #15)
> > Я еще чуть потестирую и, если нет возражений, отправлю.
> Константа GPT_NPARTITIONS в исходниках не определена, Вы задали её в
> заголовочном файле? Константа EVMS_VSECTOR_SIZE определена лишь в одном
> месте как 512 байт, а это значит, что проект вряд ли поддерживает настоящие
> "Blue 4Kn" диски и в структуру ld.geometry может приезжать совсем
> неправильное значение bytes_per_sector. Собственно, варианта всего два: 512
> и 4096 и оба лежат в /sys/dev/block/$major:$minor/queue/logical_block_size
> либо его можно получить через ioctl() как в $(blockdev --getss /dev/$NAME).
> 
> Вы правильно делаете, что отвязываете формулу от данных в ld.geometry, но
> это нужно делать до конца либо иметь понимание, что в неё попадают
> правильные данные. Вообще правильная формула должна исходить из
> спецификации, тогда всё просто -- pt_size=16384 скорее константа и она же
> возможный минимум. При делении этого числа на bytes_per_sector всегда
> получим либо 32 (для 512-байтных секторов), либо 4 (для "Blue 4Kn" дисков).
> А значит и first_lba всегда зависит по сути от единственного флага blue4kn.
> 
> Но я боюсь, что тестирование на дисках 4K покажет несовместимость libevms и
> по другим структурам. Актуализировать же код начала 2000-х придётся долго и
> мучительно. :-)

GPT_NPARTITIONS определяется в gpt.h

В ld->geometry.bytes_per_sector теоретически должен попадать результат вот этой функции http://git.altlinux.org/people/ptrnine/packages/?p=evms.git;a=blob;f=plugins/disk/localdskmgr.c;h=d38631c24b251e8d0ae877d1145e99117a111bf0;hb=HEAD#l1649
Но вы правы, сейчас в коде куча мест, где используется просто константа EVMS_VSECTOR_SIZE или даже сдвиг на EVMS_VSECTOR_SIZE_SHIFT вместо умножения/деления (экономим на спичках?). И поэтому стоит либо править все эти места, либо наоборот заменить здесь bytes_per_sector на EVMS_VSECTOR_SIZE (уже хотя бы для консистентности).
Comment 18 Repository Robot 2021-01-18 17:53:34 MSK
evms-2.5.5-alt49 -> sisyphus:

 Mon Jan 18 2021 Slava Aseev <ptrnine@altlinux> 2.5.5-alt49
 - plugins/gpt: fix start_useable and pt_count calculation
   (closes: #39385)
 - plugins/md: fix bitwise inversions