<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="https://www.w3.org/2005/Atom">
  <channel>
    <title>laserattack</title>
    <description>Blog of serr aka laserattack</description>
    <link>https://laserattack.net/</link>
    <atom:link href="https://laserattack.net/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 14 Oct 2026 18:59:16 +0000</pubDate>
    <lastBuildDate>Wed, 14 Oct 2026 18:59:16 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Быстрый git clone только нужных файлов/папок</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Быстрый git clone только нужных файлов/папок&lt;/li&gt;
  &lt;li&gt;TAGS: it, git, guide&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;проблема&quot;&gt;Проблема&lt;/h1&gt;

&lt;p&gt;Репозиторий содержит жирный бинарник (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coomer.gif&lt;/code&gt;, десятки МБ),
который тянется при обычном &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; и нужен только для красоты в
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt;. Но я хочу скачать только код и мейкфайл&lt;/p&gt;

&lt;h1 id=&quot;решение&quot;&gt;Решение&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt; - сервер отдаёт только коммиты и деревья (имена
файлов), но НЕ содержимое файлов (блобы).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-checkout&lt;/code&gt; - не разворачивать рабочее дерево после клона, иначе
git сразу полезет докачивать все блобы (включая гифку).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--depth=1&lt;/code&gt; можно добавить, если не нужна история - будет ещё
быстрее.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/temp
[serr@lap]-&amp;gt; git clone --filter=blob:none --no-checkout https://github.com/Mr-Emacs/coomer
Cloning into &apos;coomer&apos;...
remote: Enumerating objects: 75, done.
remote: Counting objects: 100% (60/60), done.
remote: Compressing objects: 100% (50/50), done.
remote: Total 75 (delta 14), reused 50 (delta 7), pack-reused 15 (from 1)
Receiving objects: 100% (75/75), 51.38 KiB | 125.00 KiB/s, done.
Resolving deltas: 100% (18/18), done.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git ls-tree -r --name-only HEAD&lt;/code&gt; - показывает плоский список всех
файлов в текущем коммите, без выкачивания содержимого. Нужно, чтобы
понять, какие пути вообще есть и что тянуть.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/temp
[serr@lap]-&amp;gt; cd coomer/
~/projects/temp/coomer
[serr@lap]-&amp;gt; git ls-tree -r --name-only HEAD
.gitignore
LICENSE
Makefile
README.md
coomer.1.gz
coomer.gif
src/cm_gl.h
src/cm_main.c
src/cm_shaders.h
src/stb_image_write.h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git restore --source=HEAD -- &amp;lt;пути&amp;gt;&lt;/code&gt; - вытаскивает указанные пути
из коммита &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt; в рабочее дерево.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/temp/coomer
[serr@lap]-&amp;gt; git restore --source=HEAD -- src Makefile
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 585 bytes | 585.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 1 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 913 bytes | 913.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 7.37 KiB | 7.37 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 1 (from 1)
Receiving objects: 100% (1/1), 601 bytes | 601.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 1 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 19.91 KiB | 19.91 MiB/s, done.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 14 Oct 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/10/14/fast-git-clone</link>
        <guid isPermaLink="true">https://laserattack.net/2026/10/14/fast-git-clone</guid>
        
        
      </item>
    
      <item>
        <title>Проблема с определением заряда батареи на HP Victus 16</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Проблема с определением заряда батареи на HP Victus 16&lt;/li&gt;
  &lt;li&gt;TAGS: linux, it, personal, guide, hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;система&quot;&gt;Система&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Модель:&lt;/strong&gt; HP Victus 16-e0xxx&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ОС:&lt;/strong&gt; Void Linux&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ядро:&lt;/strong&gt; 6.18.45_1&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Батарея&lt;/strong&gt;: 4-cell Li-ion polymer&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;проблема&quot;&gt;Проблема&lt;/h1&gt;

&lt;p&gt;системный мониторинг батареи всегда показывает &lt;strong&gt;100%&lt;/strong&gt;, независимо от
реального уровня заряда&lt;/p&gt;

&lt;h1 id=&quot;симптомы&quot;&gt;Симптомы&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/energy_now&lt;/code&gt; и
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/energy_full&lt;/code&gt; показывают одинаковые
значения (56672000)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/power_now&lt;/code&gt; не читается (“No such
device”)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/status&lt;/code&gt; меняется
(Charging/Discharging/Full и тп)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/voltage_now&lt;/code&gt; меняется (работает)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/capacity&lt;/code&gt; всегда 100&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/class/power_supply/BAT0/capacity_level&lt;/code&gt; всегда Full&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;диагностика&quot;&gt;Диагностика&lt;/h1&gt;

&lt;h2 id=&quot;acpi-запрос-_bst-battery-status&quot;&gt;ACPI запрос _BST (Battery Status)&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &apos;\_SB_.BAT0._BST&apos; | sudo tee /proc/acpi/call
sudo cat /proc/acpi/call
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вывод: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0x0, 0xffffffff, 0xdd60, 0x4173]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;расшифровка:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xffffffff&lt;/code&gt; — скорость разряда (&lt;strong&gt;неизвестно&lt;/strong&gt;, контроллер не может
измерить ток)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xdd60&lt;/code&gt; — Оставшаяся ёмкость (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;56672 mWh&lt;/code&gt;, &lt;strong&gt;застыло&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x4173&lt;/code&gt; — напряжение (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;16755 mV&lt;/code&gt;, &lt;strong&gt;работает&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;acpi-запрос-_bif-battery-information&quot;&gt;ACPI запрос _BIF (Battery Information)&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &apos;\_SB_.BAT0._BIF&apos; | sudo tee /proc/acpi/call
sudo cat /proc/acpi/call
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вывод &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0x1, 0xdd60, 0xdd60, 0x1, 0x3c28, 0x1a96, 0xf83, 0x1fe, 0x8d,
&quot;Primary&quot;, &quot; &quot;, &quot;LION&quot;, &quot;Hewlett-Packard&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xdd60&lt;/code&gt; — проектная емкость (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;56672 mAh&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xdd60&lt;/code&gt; — максимальная емкость при полном заряде (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;56672 mAh&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1&lt;/code&gt; — технология (1 = перезаряжаемая)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x3c28&lt;/code&gt; — проектное напряжение, типо среднее при работе (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15400mV&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1a96&lt;/code&gt; — предупреждение о низком заряде (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6806 mAh&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xf83&lt;/code&gt; — критически низкий заряд (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3971 mAh&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;остальное не важно&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;причина&quot;&gt;Причина&lt;/h1&gt;

&lt;p&gt;контроллер батареи (EC) не может измерить ток (скорость
разряда/заряда), из-за чего:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Не обновляется счётчик энергии (energy_now)&lt;/li&gt;
  &lt;li&gt;Система не может вычислить реальный процент заряда&lt;/li&gt;
  &lt;li&gt;power_now недоступен&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;при этом напряжение измеряется напрямую и работает корректно&lt;/p&gt;

&lt;h1 id=&quot;решение&quot;&gt;Решение&lt;/h1&gt;

&lt;p&gt;использовать напряжение батареи для оценки процента заряда вместо
неработающего счётчика энергии&lt;/p&gt;

&lt;p&gt;скрипт который пишет текущий вольтаж в системный лог (socklog)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

VOLTAGE=$(cat /sys/class/power_supply/BAT0/voltage_now)
VOLTAGE_MV=$((VOLTAGE / 1000))

logger -t &quot;current battery voltage&quot; &quot;${VOLTAGE_MV}&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;поставил его на крон раз в минуту, чтобы всегда можно было посмотреть&lt;/p&gt;

&lt;p&gt;таким образом можно будет спустя какое то время на зарядке узнать
максимальное значение, и его использовать типо как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;100%&lt;/code&gt; заряда
батареи, а потом разрядить ноут и посмотреть минимальное и
использовать его как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0%&lt;/code&gt; заряда. и текущий процент считать
относительно их&lt;/p&gt;

&lt;p&gt;конечно &lt;strong&gt;измерение заряда на основе напряжения дает погрешность в
точности&lt;/strong&gt;, но это хотя бы что то&lt;/p&gt;

&lt;p&gt;вообще нашел &lt;a href=&quot;https://en.wikipedia.org/wiki/Lithium-ion_battery&quot;&gt;статью&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;там написано&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Lithium-ion cells are susceptible to stress by voltage ranges
outside of safe ones between 2.5 and 3.65/4.1/4.2 or 4.35 V
(depending on the components of the cell)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;т.е. минимум &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.5 V&lt;/code&gt; на элемент, на 4 элемента как у меня в батарее
это значит &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10 V&lt;/code&gt; а максимум зависит от конкретной батареи, нейросеть
говорит что для моей &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4.2 V&lt;/code&gt;, т.е. на 4 элемента это &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;16.8 V&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;по моим логам максимум где то &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;16.76&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; sucklog user | grep battery
2026-09-05T12:31:01.15228 user.notice: Sep  5 15:31:01 current battery voltage: 16765
2026-09-05T12:32:01.15999 user.notice: Sep  5 15:32:01 current battery voltage: 16765
2026-09-05T12:33:01.16847 user.notice: Sep  5 15:33:01 current battery voltage: 16764
2026-09-05T12:34:01.17606 user.notice: Sep  5 15:34:01 current battery voltage: 16764
2026-09-05T12:35:01.18485 user.notice: Sep  5 15:35:01 current battery voltage: 16764
2026-09-05T12:36:01.19382 user.notice: Sep  5 15:36:01 current battery voltage: 16764
2026-09-05T12:37:01.20235 user.notice: Sep  5 15:37:01 current battery voltage: 16764
2026-09-05T12:38:01.20998 user.notice: Sep  5 15:38:01 current battery voltage: 16764
2026-09-05T12:39:01.21902 user.notice: Sep  5 15:39:01 current battery voltage: 16764
2026-09-05T12:40:01.22840 user.notice: Sep  5 15:40:01 current battery voltage: 16764
2026-09-05T12:41:01.23742 user.notice: Sep  5 15:41:01 current battery voltage: 16765
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;а &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10 V&lt;/code&gt; это конечно абсолютный минимум до которого не опустится&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.highstar.com/blog/lithium-ion-battery-cell-voltage-nominal-cutoff-charging-guide&quot;&gt;тут&lt;/a&gt;
например сказано&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Typically, Li-ion cells operate with a discharge cutoff around 3.0V&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;т.е. это &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12 V&lt;/code&gt; на 4 элемента, такое и возьму за минимум пока что,
потом мб подкорректирую по логам&lt;/p&gt;

&lt;p&gt;т.е. пока что вот так считаю зарядку&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

MAX_VOLTAGE=16800
MIN_VOLTAGE=12000

VOLTAGE=$(cat /sys/class/power_supply/BAT0/voltage_now)
VOLTAGE_MV=$((VOLTAGE / 1000))

PERCENT=$(echo &quot;scale=0; ($VOLTAGE_MV - $MIN_VOLTAGE) * 100 / ($MAX_VOLTAGE - $MIN_VOLTAGE)&quot; | bc)

[ $PERCENT -gt 100 ] &amp;amp;&amp;amp; PERCENT=100
[ $PERCENT -lt 0 ] &amp;amp;&amp;amp; PERCENT=0

echo &quot;BAT ${PERCENT}% (${VOLTAGE_MV}mV)&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 05 Sep 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/09/05/bat-problem</link>
        <guid isPermaLink="true">https://laserattack.net/2026/09/05/bat-problem</guid>
        
        
      </item>
    
      <item>
        <title>Константы в C++</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Константы в C++&lt;/li&gt;
  &lt;li&gt;TAGS: it, c++, cpp&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;указатели-на-переменные-базовых-типов&quot;&gt;Указатели на переменные базовых типов&lt;/h1&gt;

&lt;h2 id=&quot;случай-1&quot;&gt;Случай 1&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Переменная&lt;/strong&gt;: &lt;em&gt;неконстантная&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Указатель&lt;/strong&gt;: &lt;em&gt;неконстантный&lt;/em&gt;, указывающий на неконстантную
переменную&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int a = 10;
int *p = &amp;amp;a; // OK
p--; // OK
p++; // OK
(*p)++; // OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-2&quot;&gt;Случай 2&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Переменная&lt;/strong&gt;: &lt;em&gt;константная&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Указатель&lt;/strong&gt;: &lt;em&gt;неконстантный&lt;/em&gt;, указывающий на неконстантную
переменную&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: если переменная константная то указатель должен быть
указывающим на константную переменную&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const int a = 10;
int *p = &amp;amp;a; // error: invalid conversion from &apos;const int*&apos; to &apos;int*&apos;
p--;
p++;
(*p)++;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-3&quot;&gt;Случай 3&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Переменная&lt;/strong&gt;: &lt;em&gt;константная&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Указатель&lt;/strong&gt;: &lt;em&gt;неконстантный&lt;/em&gt;, указывающий на константную переменную&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: нельзя через неконстантный указатель, указывающий на
константную память, изменить память на которую он указывает. Сам
указатель при этом менять можно&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const int a = 10;
const int *p = &amp;amp;a; // OK
p--; // OK
p++; // OK
(*p)++; // error: increment of read-only location &apos;* p&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;сначала подумал что &lt;em&gt;read-only location&lt;/em&gt; означает RO-секцию типо
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rodata&lt;/code&gt;, куда поместился литерал. но в общем случае это то не
означает, потому что тут ошибка та же&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void f(const int *p) {
    (*p)++; // error: increment of read-only location &apos;* p&apos;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;т.е. read-only тут не факт что уровня ОС, под read-only может иметься
в виду гарантия компилятора не менять константу&lt;/p&gt;

&lt;h2 id=&quot;случай-4&quot;&gt;Случай 4&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Переменная&lt;/strong&gt;: &lt;em&gt;неконстантная&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Указатель&lt;/strong&gt;: &lt;em&gt;константный&lt;/em&gt;, указывающий на неконстантную
переменную&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: через константный указатель, указывающий на неконстантную
память, можно изменить память на которую он указывает. Сам указатель
при этом менять нельзя&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int a = 10;
int *const p = &amp;amp;a; // OK
(*p)++; // OK
p--; // error: decrement of read-only variable &apos;p&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-5&quot;&gt;Случай 5&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Переменная&lt;/strong&gt;: &lt;em&gt;константная&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Указатель&lt;/strong&gt;: &lt;em&gt;константный&lt;/em&gt;, указывающий на константную переменную&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: если указатель константный и указывает на константную
память, то нельзя ни менять через него память на которую он
указывает, ни менять сам указатель&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const int a = 10;
const int *const p = &amp;amp;a;
(*p)++; // error: increment of read-only location &apos;*(const int*)p&apos;
p--; // error: decrement of read-only variable &apos;p&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;пользовательские-типы-константные-и-неконстантные-объекты&quot;&gt;Пользовательские типы: константные и неконстантные объекты&lt;/h1&gt;

&lt;h2 id=&quot;случай-1-1&quot;&gt;Случай 1&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: нельзя создать константный объект, если его тип не имеет
конструктора. Неконстантный объект создать при этом можно&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    int data;
};

int main() {
    Data a; // OK
    const Data b; // error: uninitialized &apos;const b&apos;
                  // note: &apos;const struct Data&apos; has no user-provided default constructor
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Если определяю конструктор, то все ок&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    int data;
    Data() : data() {}
};

int main() {
    Data a; // OK
    const Data b; // OK
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-2-1&quot;&gt;Случай 2&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: если объект константный, то даже неконстантные его поля
менять нельзя (если не пометить их как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mutable&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    int data;
    Data() : data() {}
};

int main() {
    Data a;
    const Data b;
    a.data = 1;
    b.data = 1; // error: assignment of member &apos;Data::data&apos; in read-only object
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;если поле помечено как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mutable&lt;/code&gt; то его можно менять даже если объект
константный&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    mutable int data;
    Data() : data() {}
};

int main() {
    Data a;
    const Data b;
    a.data = 1;
    b.data = 1;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-3-1&quot;&gt;Случай 3&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: если объект константный, то даже неконстантные его поля
менять нельзя (если не пометить их как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mutable&lt;/code&gt;), но если поле -
указатель, то можно менять память на которую он указывает, но не сам
указатель&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    int *data;
    Data() : data(new int[3]) {}
    ~Data() { delete[] data; }
};

int main() {
    Data a; // OK
    const Data b; // OK
    a.data[1] = 1; // OK
    b.data[1] = 1; // OK
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-4-1&quot;&gt;Случай 4&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: не-const метод нельзя вызвать на const объекте&lt;/p&gt;

&lt;p&gt;матчасть: любой метод неявно принимает адрес объекта,
т.е. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj.method()&lt;/code&gt; компилятор превращает образно говоря в
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class::method(&amp;amp;obj)&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

struct Data {
    int *data;
    int len;
    Data() {
        data = new int[3]{1, 2, 3};
        len = 3;
    }
    ~Data() { delete[] data; }
    void print() {
        for (int i = 0; i &amp;lt; len; i++) {
            std::cout &amp;lt;&amp;lt; data[i];
        }
    }
};

int main() {
    const Data a;
    a.print(); // error: passing &apos;const Data&apos; as &apos;this&apos; argument discards qualifiers
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Когда вызывается &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.print()&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Data a&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Неявный аргумент &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt; ожидается как &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data *&lt;/code&gt; (не const)&lt;/li&gt;
  &lt;li&gt;Передача &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Data *&lt;/code&gt; в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data *&lt;/code&gt; — потеря const, про что и
написано в ошибке&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;если определяю метод как константный то все ок&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

struct Data {
    int *data;
    int len;
    Data() {
        data = new int[3]{1, 2, 3};
        len = 3;
    }
    ~Data() { delete[] data; }
    void print() const {
        for (int i = 0; i &amp;lt; len; i++) {
            std::cout &amp;lt;&amp;lt; data[i];
        }
    }
};

int main() {
    const Data a;
    a.print(); // OK
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;если не менять метод то можно еще так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

struct Data {
    int *data;
    int len;
    Data() {
        data = new int[3]{1, 2, 3};
        len = 3;
    }
    ~Data() { delete[] data; }
    void print() {
        for (int i = 0; i &amp;lt; len; i++) {
            std::cout &amp;lt;&amp;lt; data[i];
        }
    }
};

int main() {
    const Data a; // OK
    const_cast&amp;lt;Data&amp;amp;&amp;gt;(a).print(); // OK
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;случай-5-1&quot;&gt;Случай 5&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Вывод&lt;/strong&gt;: существует перегрузка по &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct Data {
    void print() {
        std::cout &amp;lt;&amp;lt; &quot;non-const\n&quot;;
    }

    void print() const {
        std::cout &amp;lt;&amp;lt; &quot;const\n&quot;;
    }
};

int main() {
    Data a;
    const Data b;

    a.print(); // &quot;non-const&quot;
    b.print(); // &quot;const&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 25 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/25/cpp-const</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/25/cpp-const</guid>
        
        
      </item>
    
      <item>
        <title>Как я уменьшил в 2 раза число системных вызовов newfstatat в mado</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Как я уменьшил в 2 раза число системных вызовов newfstatat в mado&lt;/li&gt;
  &lt;li&gt;TAGS: it, linux, syscall, performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;что-за-mado&quot;&gt;Что за mado?&lt;/h1&gt;

&lt;p&gt;Программа, которую я периодически пишу и использую для ведения заметок
и задач: &lt;a href=&quot;https://github.com/laserattack/mado&quot;&gt;mado&lt;/a&gt; - markdown
organizer for Linux&lt;/p&gt;

&lt;h1 id=&quot;проблема&quot;&gt;Проблема&lt;/h1&gt;

&lt;p&gt;Я создал 10000 заметок, которые программа должна обработать и вот что
я обнаружил:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 36.26    0.086568           4     20090           newfstatat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;20000 системные вызовов stat на 10000 записей. при этом явно в
программе stat вызывается один раз на запись. так с чего системных
вызовов в 2 раза больше чем должно быть?&lt;/p&gt;

&lt;h1 id=&quot;расследование&quot;&gt;Расследование&lt;/h1&gt;

&lt;h2 id=&quot;локализую-проблему&quot;&gt;Локализую проблему&lt;/h2&gt;

&lt;p&gt;оказалось для каждой записи вот что происходит&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO/20260107T020900/MAIN.md&quot;, {st_mode=S_IFREG|0644, st_size=2543, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x11c72]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x147ce]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa203]
 &amp;gt; /home/serr/projects/mado/mado(main+0xdd) [0x764d]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7831]
newfstatat(AT_FDCWD, &quot;/etc/localtime&quot;, {st_mode=S_IFREG|0644, st_size=1535, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /usr/lib/libc.so.6(__tzfile_read+0xec) [0xcae0c]
 &amp;gt; /usr/lib/libc.so.6(tzset_internal+0x154) [0xca9a4]
 &amp;gt; /usr/lib/libc.so.6(__tz_convert+0x53) [0xcaba3]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x1281d]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x147ce]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa203]
 &amp;gt; /home/serr/projects/mado/mado(main+0xdd) [0x764d]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;посмотрю что там&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0x0000000000012818 &amp;lt;+5896&amp;gt;:	call   0x5260 &amp;lt;localtime@plt&amp;gt;
0x000000000001281d &amp;lt;+5901&amp;gt;:	lea    0x9e1e(%rip),%rdx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;т.е. первый системный вызов - тот, который я делаю явно, а второй -
получение информации о таймзоне через функцию &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;вот проблемный участок кода&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if (need_mtime) {
    struct stat st;
    if (stat(entry_file.c_str(), &amp;amp;st) == 0) {
        std::tm *tm = std::localtime(&amp;amp;st.st_mtime);
        char buf[16];
        std::strftime(buf, sizeof(buf), &quot;%Y%m%dT%H%M%S&quot;, tm);
        entry-&amp;gt;mtime = buf;
    } else {
        return nullptr;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;получается что функция &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::localtime&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;std::tm *tm = std::localtime(&amp;amp;st.st_mtime);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;для каждой записи заново узнает информацию о таймзоне&lt;/p&gt;

&lt;h2 id=&quot;откуда-берется-локальное-время&quot;&gt;Откуда берется локальное время&lt;/h2&gt;

&lt;p&gt;В системах Linux информация о часовом поясе (таймзоне) хранится в
файле &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/localtime&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Когда программа запрашивает локальное время, библиотека &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libc&lt;/code&gt; должна
знать:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Смещение от UTC&lt;/li&gt;
  &lt;li&gt;Правила перехода на летнее/зимнее время&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;вот тут
&lt;a href=&quot;https://man7.org/linux/man-pages/man3/localtime.3.html&quot;&gt;man7.org/linux/man-pages/man3/localtime.3.html&lt;/a&gt;
есть такой абзац&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The localtime() function converts the calendar time timep to
broken-down time representation, expressed relative to the user’s
specified timezone. The function also sets the external variables
tzname, timezone, and daylight as if it called tzset(3). The return
value points to a statically allocated struct which might be
overwritten by subsequent calls to any of the date and time
functions. The localtime_r() function does the same, but stores the
data in a user-supplied struct. It need not set tzname, timezone,
and daylight.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ключевое тут про &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime&lt;/code&gt; вот что&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The function also sets the external variables tzname, timezone, and
daylight as if it called tzset(3)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;т.е. она действительно при каждом вызове читает информацию о таймзоне,
а значит обращается к &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/localtime&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;тут же написано про другую функцию - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime_r&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The localtime_r() function does the same, but stores the data in a
user-supplied struct. It need not set tzname, timezone, and
daylight.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;а вот она не читает информацию о таймзоне при каждом вызове&lt;/p&gt;

&lt;h2 id=&quot;расследую-далее&quot;&gt;Расследую далее&lt;/h2&gt;

&lt;p&gt;поменял &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::localtime&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime_r&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;modified   libmado/mado.cpp
@@ -228,9 +228,10 @@ Mado_Entry::parse(const Mado_Config *cfg,
     if (need_mtime) {
         struct stat st;
         if (stat(entry_file.c_str(), &amp;amp;st) == 0) {
-            std::tm *tm = std::localtime(&amp;amp;st.st_mtime);
+            struct tm tm_result;
+            localtime_r(&amp;amp;st.st_mtime, &amp;amp;tm_result);
             char buf[16];
-            std::strftime(buf, sizeof(buf), &quot;%Y%m%dT%H%M%S&quot;, tm);
+            std::strftime(buf, sizeof(buf), &quot;%Y%m%dT%H%M%S&quot;, &amp;amp;tm_result);
             entry-&amp;gt;mtime = buf;
         } else {
             return nullptr;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;и действительно получаю уменьшение числа системных вызовов в 2 раза&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 25.18    0.043956           4     10047           newfstatat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;но обращение к &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/localtime&lt;/code&gt; все таки происходит&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openat(AT_FDCWD, &quot;/etc/localtime&quot;, O_RDONLY|O_CLOEXEC) = 4
 &amp;gt; /usr/lib/libc.so.6(__open64_nocancel+0x3e) [0xfbdee]
 &amp;gt; /usr/lib/libc.so.6(_IO_file_open+0xb5) [0x84bd5]
 &amp;gt; /usr/lib/libc.so.6(_IO_file_fopen+0xc5) [0x84cb5]
 &amp;gt; /usr/lib/libc.so.6(__fopen_internal+0x79) [0x79459]
 &amp;gt; /usr/lib/libc.so.6(__tzfile_read+0x103) [0xcae23]
 &amp;gt; /usr/lib/libc.so.6(tzset_internal+0x154) [0xca9a4]
 &amp;gt; /usr/lib/libc.so.6(__tz_convert+0x53) [0xcaba3]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x12828]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x147ce]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa203]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вот оно тут&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0x0000000000012823 &amp;lt;+5907&amp;gt;:	call   0x51c0 &amp;lt;localtime_r@plt&amp;gt;
0x0000000000012828 &amp;lt;+5912&amp;gt;:	mov    %r13,%rcx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;но происходит всего 1 раз за время работы программы. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime_r&lt;/code&gt;
обращается к файлу если внутренние переменные с инфой о таймзоне не
установлены, но если они установлены - не обращается, а просто
использует инфу из них&lt;/p&gt;

&lt;p&gt;вот что еще есть в мануале&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;According to POSIX.1, localtime() is required to behave as though
tzset(3) was called, while localtime_r() does not have this
requirement.  For portable code, tzset(3) should be called before
localtime_r()&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;т.е. рекомендуется все таки явно инициализировать внутренние
переменные таймзоны через &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tzset()&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;что-в-итоге&quot;&gt;Что в итоге&lt;/h1&gt;

&lt;p&gt;Заменив &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::localtime()&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localtime_r()&lt;/code&gt; + однократный вызов
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tzset()&lt;/code&gt;, убрал лишний системный вызов &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stat()&lt;/code&gt; для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/localtime&lt;/code&gt;
на каждую запись&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Результат на 100 000 записей:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Время выполнения: &lt;strong&gt;~4.0 с -&amp;gt; ~3.5 с&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Ускорение: &lt;strong&gt;~12%&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Сокращение системных вызовов: &lt;strong&gt;100 000 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newfstatat&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 18 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/18/stat-syscall-reduce-mado</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/18/stat-syscall-reduce-mado</guid>
        
        
      </item>
    
      <item>
        <title>Нахождение утечек и узких мест в производительности программ</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Нахождение утечек и узких мест в производительности программ&lt;/li&gt;
  &lt;li&gt;TAGS: it, linux, valgrind, strace, time&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;time&quot;&gt;time&lt;/h1&gt;

&lt;p&gt;просто проверка времени работы&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/mado
[serr@lap]-&amp;gt; time mado ls &apos;s ~ ope&apos;
&amp;lt;ВЫВОД ПРОГРАММЫ&amp;gt;
real	0m5.091s
user	0m4.247s
sys	0m0.823s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;valgrind&quot;&gt;valgrind&lt;/h1&gt;

&lt;p&gt;ищет утечки, использую такой алиас&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/mado
[serr@lap]-&amp;gt; cat ~/.bashrc | grep valgall
alias valgall=&apos;valgrind --leak-check=full --show-leak-kinds=all&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;пример&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/mado
[serr@lap]-&amp;gt; valgall mado ls &apos;s ~ ope&apos;
==8710== Memcheck, a memory error detector
==8710== Copyright (C) 2002-2024, and GNU GPL&apos;d, by Julian Seward et al.
==8710== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==8710== Command: mado ls s\ ~\ ope
==8710==
&amp;lt;ВЫВОД ПРОГРАММЫ&amp;gt;
==8710==
==8710== HEAP SUMMARY:
==8710==     in use at exit: 0 bytes in 0 blocks
==8710==   total heap usage: 2,601,468 allocs, 2,601,468 frees, 1,243,906,158 bytes allocated
==8710==
==8710== All heap blocks were freed -- no leaks are possible
==8710==
==8710== For lists of detected and suppressed errors, rerun with: -s
==8710== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;strace&quot;&gt;strace&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; — это утилита для трассировки системных вызовов
(syscalls). Позволяет увидеть, с какими файлами, сокетами, процессами
и памятью взаимодействует программа на уровне ядра. Используется для
поиска узких мест, связанных с I/O, работой с файловой системой и
сетевыми операциями&lt;/p&gt;

&lt;p&gt;пример&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/mado
[serr@lap]-&amp;gt; strace -c mado ls &apos;s ~ ope&apos;
&amp;lt;ВЫВОД ПРОГРАММЫ&amp;gt;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.02    0.852957           4    200090           newfstatat
 24.41    0.520218           5    100063        12 openat
 17.66    0.376334           3    100091           read
 15.83    0.337496           3    100051           close
  1.87    0.039811         321       124           getdents64
  0.18    0.003920           5       681           brk
  0.02    0.000450          90         5           munmap
  0.01    0.000196           7        26           mmap
  0.00    0.000048           8         6           mprotect
  0.00    0.000023           2         9           fstat
  0.00    0.000008           4         2           pread64
  0.00    0.000006           3         2           fcntl
  0.00    0.000006           6         1           getcwd
  0.00    0.000006           6         1           getrandom
  0.00    0.000005           5         1           futex
  0.00    0.000004           4         1           lseek
  0.00    0.000004           4         1           arch_prctl
  0.00    0.000004           4         1           set_tid_address
  0.00    0.000004           4         1           set_robust_list
  0.00    0.000004           4         1           prlimit64
  0.00    0.000003           3         1           rseq
  0.00    0.000000           0         2           write
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
------ ----------- ----------- --------- --------- ----------------
100.00    2.131507           4    501163        13 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;далее можно узнать подробности про конкретный syscall, пишется
например с какими аргументами вызывается&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/projects/mado
[serr@lap]-&amp;gt; strace -k -e trace=newfstatat mado ls &apos;s ~ ope&apos; 2&amp;gt;&amp;amp;1 | head -50
newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO&quot;, {st_mode=S_IFDIR|0755, st_size=3547136, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cd760]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cddf9]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0xebe6]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x133) [0x9a23]
 &amp;gt; /home/serr/projects/mado/mado(main+0xe2) [0x7462]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7631]
newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO&quot;, {st_mode=S_IFDIR|0755, st_size=3547136, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cd760]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cddf9]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x13a72]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa003]
 &amp;gt; /home/serr/projects/mado/mado(main+0xe2) [0x7462]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7631]
newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO&quot;, {st_mode=S_IFDIR|0755, st_size=3547136, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cd760]
 &amp;gt; /usr/lib/libstdc++.so.6.0.33(+0x0) [0x1cddf9]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x13ab0]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa003]
 &amp;gt; /home/serr/projects/mado/mado(main+0xe2) [0x7462]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7631]
newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO/20260110T143500/MAIN.md&quot;, {st_mode=S_IFREG|0644, st_size=2544, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x113f2]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x13eaa]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa003]
 &amp;gt; /home/serr/projects/mado/mado(main+0xe2) [0x7462]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7631]
newfstatat(AT_FDCWD, &quot;/home/serr/projects/mado/MADO/20260220T003100/MAIN.md&quot;, {st_mode=S_IFREG|0644, st_size=2544, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x113f2]
 &amp;gt; /home/serr/projects/mado/mado(+0x0) [0x13eaa]
 &amp;gt; /home/serr/projects/mado/mado(_ZL8cmd_listiPPc+0x713) [0xa003]
 &amp;gt; /home/serr/projects/mado/mado(main+0xe2) [0x7462]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_call_main+0x7c) [0x2abfc]
 &amp;gt; /usr/lib/libc.so.6(__libc_start_main+0x85) [0x2acb5]
 &amp;gt; /home/serr/projects/mado/mado(_start+0x21) [0x7631]
newfstatat(AT_FDCWD, &quot;/etc/localtime&quot;, {st_mode=S_IFREG|0644, st_size=1535, ...}, 0) = 0
 &amp;gt; /usr/lib/libc.so.6(fstatat+0xa) [0xf76aa]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 17 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/17/performance-leaks</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/17/performance-leaks</guid>
        
        
      </item>
    
      <item>
        <title>Работа с PGP ключами</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Работа с PGP ключами&lt;/li&gt;
  &lt;li&gt;TAGS: pgp, cipher, privacy, guide&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;создание-мастерключей&quot;&gt;Создание мастерключей&lt;/h1&gt;

&lt;p&gt;Мастерключи должны храниться только в зашифрованной ФС, я буду
использовать &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gocryptfs&lt;/code&gt; - оверлейную зашифрованную файловую систему&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir ~/mnt/gocryptfs/pgp-keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;далее запускаю процесс создания ключа&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --full-generate-key
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: keybox &apos;/home/serr/mnt/gocryptfs/pgp-keys/pubring.kbx&apos; created
Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 10
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection?
Please specify how long the key should be valid.
         0 = key does not expire
      &amp;lt;n&amp;gt;  = key expires in n days
      &amp;lt;n&amp;gt;w = key expires in n weeks
      &amp;lt;n&amp;gt;m = key expires in n months
      &amp;lt;n&amp;gt;y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Primary Key
Email address:
Comment: Clean
You selected this USER-ID:
    &quot;Primary Key (Clean)&quot;

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/serr/mnt/gocryptfs/pgp-keys/trustdb.gpg: trustdb created
gpg: directory &apos;/home/serr/mnt/gocryptfs/pgp-keys/openpgp-revocs.d&apos; created
gpg: revocation certificate stored as &apos;/home/serr/mnt/gocryptfs/pgp-keys/openpgp-revocs.d/0C73E648FEEA465E8F6493AF38A22EA086864C8F.rev&apos;
public and secret key created and signed.

pub   ed25519 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
uid                      Primary Key (Clean)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;важно что я выбирал&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  (10) ECC (sign only)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;т.е. мастерключ будет использоваться только для подписи субключей&lt;/p&gt;

&lt;h1 id=&quot;выпуск-субключей&quot;&gt;Выпуск субключей&lt;/h1&gt;

&lt;p&gt;Создаю субключ для шифрования&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --edit-key &quot;Primary Key (Clean)&quot;
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (10) ECC (sign only)
  (12) ECC (encrypt only)
  (14) Existing key from card
Your selection? 12
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection?
Please specify how long the key should be valid.
         0 = key does not expire
      &amp;lt;n&amp;gt;  = key expires in n days
      &amp;lt;n&amp;gt;w = key expires in n weeks
      &amp;lt;n&amp;gt;m = key expires in n months
      &amp;lt;n&amp;gt;y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sun 15 Aug 2027 02:12:50 PM MSK
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;тут важно что я выбрал&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  (12) ECC (encrypt only)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;создания пассфразы тут не было, зашифровано по умолчанию той же
пассфразой что и мастерключ&lt;/p&gt;

&lt;p&gt;в связке так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --list-secret-keys --with-subkey-fingerprint &quot;Primary Key (Clean)&quot;
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
sec   ed25519 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
uid           [ultimate] Primary Key (Clean)
ssb   cv25519 2026-08-15 [E] [expires: 2027-08-15]
      9A819283B120FA7E83523B2E5DBD0FD73ACA057F
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;еще выпущу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sign&lt;/code&gt; ключ для использования в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --edit-key &quot;Primary Key (Clean)&quot;
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (10) ECC (sign only)
  (12) ECC (encrypt only)
  (14) Existing key from card
Your selection? 10
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection?
Please specify how long the key should be valid.
         0 = key does not expire
      &amp;lt;n&amp;gt;  = key expires in n days
      &amp;lt;n&amp;gt;w = key expires in n weeks
      &amp;lt;n&amp;gt;m = key expires in n months
      &amp;lt;n&amp;gt;y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sun 15 Aug 2027 02:24:57 PM MSK
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb  ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: S
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;но сейчас экспорт в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; не работает&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --export-ssh-key 7CB4AF1623EBFD0D
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg: key &quot;7CB4AF1623EBFD0D&quot; not found: Unusable public key
gpg: export as ssh key failed: Unusable public key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;надо сменить тип использования с &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sign&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticate&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --edit-key &quot;Primary Key (Clean)&quot;
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb  ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: S
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; key 2

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb* ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: S
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; change-usage
Changing usage of a subkey.

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? A

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? S

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? Q

sec  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb* ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: A
[ultimate] (1). Primary Key (Clean)

gpg&amp;gt; save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вот теперь экспорт в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --export-ssh-key 7CB4AF1623EBFD0D
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILDuVae20rXvJDEHZUqKjUYJ7837SFL2HG5O2xNa4XUE openpgp:0x23EBFD0D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вообще типы использования такие&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Certify (C)&lt;/strong&gt; - Подписывать другие ключи (сертификация)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign (S)&lt;/strong&gt; - Подписывать файлы, сообщения, git-коммиты&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Encrypt (E)&lt;/strong&gt; - Шифровать файлы, сообщения&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Authenticate (A)&lt;/strong&gt; - Аутентификация&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;экспорт-субключей-в-основную-систему&quot;&gt;Экспорт субключей в основную систему&lt;/h1&gt;

&lt;p&gt;мастерключ не экспортируется, он живет только в зашифрованной фс, это ключевое требование&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --homedir ~/mnt/gocryptfs/pgp-keys --export-secret-subkeys &quot;Primary Key (Clean)&quot; | gpg --import --pinentry-mode loopback
gpg: WARNING: unsafe permissions on homedir &apos;/home/serr/mnt/gocryptfs/pgp-keys&apos;
gpg: key 38A22EA086864C8F: public key &quot;Primary Key (Clean)&quot; imported
gpg: To migrate &apos;secring.gpg&apos;, with each smartcard, run: gpg --card-status
gpg: key 38A22EA086864C8F: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:  secret keys unchanged: 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;проверка&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; gpg --list-secret-keys --keyid-format LONG
gpg: checking the trustdb
gpg: no ultimately trusted keys found
/home/serr/.gnupg/pubring.kbx
-----------------------------
sec#  ed25519/38A22EA086864C8F 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
uid                 [ unknown] Primary Key (Clean)
ssb   cv25519/5DBD0FD73ACA057F 2026-08-15 [E] [expires: 2027-08-15]
ssb   ed25519/7CB4AF1623EBFD0D 2026-08-15 [A] [expires: 2027-08-15]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sec#&lt;/code&gt; - тут &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; означает что нет приватного ключа&lt;/p&gt;

&lt;p&gt;проверить можно так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/mnt/gocryptfs
[serr@lap]-&amp;gt; echo &quot;test&quot; | gpg --sign --local-user 38A22EA086864C8F
gpg: signing failed: No secret key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;без приватной части мастерключа нельзя никак редактировать ключи,
например вот тут я пытаюсь поменять срок действия ключа&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; gpg --edit-key &quot;Primary Key (Clean)&quot;
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret subkeys are available.

pub  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: unknown       validity: unknown
ssb  cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb  ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: A
ssb  ed25519/1CC271B607E17A73
     created: 2026-08-15  expires: 2027-08-15  usage: S
[ unknown] (1). Primary Key (Clean)

gpg&amp;gt; key 1

pub  ed25519/38A22EA086864C8F
     created: 2026-08-15  expires: never       usage: SC
     trust: unknown       validity: unknown
ssb* cv25519/5DBD0FD73ACA057F
     created: 2026-08-15  expires: 2027-08-15  usage: E
ssb  ed25519/7CB4AF1623EBFD0D
     created: 2026-08-15  expires: 2027-08-15  usage: A
ssb  ed25519/1CC271B607E17A73
     created: 2026-08-15  expires: 2027-08-15  usage: S
[ unknown] (1). Primary Key (Clean)

gpg&amp;gt; expire
Changing expiration time for a subkey.
Please specify how long the key should be valid.
         0 = key does not expire
      &amp;lt;n&amp;gt;  = key expires in n days
      &amp;lt;n&amp;gt;w = key expires in n weeks
      &amp;lt;n&amp;gt;m = key expires in n months
      &amp;lt;n&amp;gt;y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
gpg: signing failed: No secret key
gpg: make_keysig_packet failed: No secret key

gpg&amp;gt; quit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;сохранение-приватных-мастерключей-в-keepass&quot;&gt;Сохранение приватных мастерключей в keepass&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Публичный ключ математически выводим из приватного&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg --homedir ~/mnt/gocryptfs/pgp-keys --export-secret-keys &quot;Primary Key (Clean)&quot; &amp;gt; /tmp/primary-clean-secret.asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;и в keepassxc например аттачментсы добавляются в заметке во вкладке
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Advanced&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;оттуда же их можно сохранить в систему&lt;/p&gt;

&lt;p&gt;импорт из &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt; так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; mkdir -p /tmp/test-gpg &amp;amp;&amp;amp; chmod 700 /tmp/test-gpg
~
[serr@lap]-&amp;gt; gpg --homedir /tmp/test-gpg --import /tmp/primary-clean-secret.asc
gpg: keybox &apos;/tmp/test-gpg/pubring.kbx&apos; created
gpg: /tmp/test-gpg/trustdb.gpg: trustdb created
gpg: key 38A22EA086864C8F: public key &quot;Primary Key (Clean)&quot; imported
gpg: key 38A22EA086864C8F: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
~
[serr@lap]-&amp;gt; gpg --homedir /tmp/test-gpg --list-secret-keys --keyid-format LONG
/tmp/test-gpg/pubring.kbx
-------------------------
sec   ed25519/38A22EA086864C8F 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
uid                 [ unknown] Primary Key (Clean)
ssb   cv25519/5DBD0FD73ACA057F 2026-08-15 [E] [expires: 2027-08-15]
ssb   ed25519/7CB4AF1623EBFD0D 2026-08-15 [A] [expires: 2027-08-15]
ssb   ed25519/1CC271B607E17A73 2026-08-15 [S] [expires: 2027-08-15]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;тут импортировался полностью мастерключ ну и субключи все&lt;/p&gt;

&lt;h1 id=&quot;экспорт-и-импорт-публичного-ключа&quot;&gt;Экспорт и импорт публичного ключа&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg --export --armor &quot;Primary Key (Clean)&quot; &amp;gt; /tmp/primary-clean-public.asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;выглядит как то так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; cat /tmp/primary-clean-public.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEaoA+YRYJKwYBBAHaRw8BAQdALcylHsJhiaiG/1Z/6L6kZeABo/rgYX9TVj2i
VpTReeC0E1ByaW1hcnkgS2V5IChDbGVhbimIkwQTFgoAOxYhBAxz5kj+6kZej2ST
rziiLqCGhkyPBQJqgD5hAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJ
EDiiLqCGhkyPpuQA/2EhSFGG7YaLp1LINKNdH+kO0PNlseaQ3bE/8LQz5apJAP9F
qs5giukGNFW1WsDa2xFwGxV9dlHr3UgFF9B2YiOOBbg4BGqASZ4SCisGAQQBl1UB
BQEBB0CqHGu4KplCJul4FHH3mrh3nDVrWKXdhYCc3NpeQzL3dAMBCAeIfgQYFgoA
JhYhBAxz5kj+6kZej2STrziiLqCGhkyPBQJqgEmeAhsMBQkB4TOAAAoJEDiiLqCG
hkyPGUwA/jRI1/FCbw5ewLNd81KeaVCxOyQjQ2AN2maMOiKPSESmAP495K4crObv
3NaW48Myq3HHXHgttH2rBTOQmhHvuoROAbgzBGqATHUWCSsGAQQB2kcPAQEHQLDu
Vae20rXvJDEHZUqKjUYJ7837SFL2HG5O2xNa4XUEiPUEGBYKACYFCQHhM4AWIQQM
c+ZI/upGXo9kk684oi6ghoZMjwUCaoBNogIbIACBdiAEGRYKAB0WIQRB0v4ZIcMD
zXRqWx58tK8WI+v9DQUCaoBMdQAKCRB8tK8WI+v9DUJPAP4q0SEChxjHad+se78f
JUUPAsVXDiJP0oyXMjg2KMswUQEAsrj+cKEf0FZEp7A2c6Y5lRQan3o7BWfe9UbY
X/QjOQoJEDiiLqCGhkyPA5gBALvzO+U+otN5+MaUaP6uAWmmnvyJgUYJH8pC+4Td
eoM4AP43ijjzxlViwpBpd7t4NqYr205j3kHH6hvqpFNQk9g9AbgzBGqAUlgWCSsG
AQQB2kcPAQEHQOxTz0Y5CxSKuxK5Amcv6gCfb6DVbzvQDGG+pczDHqS1iPUEGBYK
ACYWIQQMc+ZI/upGXo9kk684oi6ghoZMjwUCaoBSWAIbAgUJAeEzgACBCRA4oi6g
hoZMj3YgBBkWCgAdFiEEowAY9aL3SZ8UdFcuHMJxtgfhenMFAmqAUlgACgkQHMJx
tgfhenMF/AEA/KJbWBOt/NQXeFXmBnqJ+G0/bAlbSSKjlTCGbmee088BAKN2hdCw
zSISFJ37A34VNzNeEbHfjXdyitrlHa8BqXEJ6uoA/isP/B1XE4U0U8+1NA4K1V6t
JLK/Jcgjlp3fy2EnxU19AP9wTn1uwrFackH+ODlKX6JQkMYDfgwakdxmHNfm55RS
DA==
=TLTG
-----END PGP PUBLIC KEY BLOCK-----
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;импорт так&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; mkdir -p /tmp/test-contact &amp;amp;&amp;amp; chmod 700 /tmp/test-contact
~
[serr@lap]-&amp;gt; gpg --homedir /tmp/test-contact --import /tmp/primary-clean-public.asc
gpg: keybox &apos;/tmp/test-contact/pubring.kbx&apos; created
gpg: /tmp/test-contact/trustdb.gpg: trustdb created
gpg: key 38A22EA086864C8F: public key &quot;Primary Key (Clean)&quot; imported
gpg: Total number processed: 1
gpg:               imported: 1
~
[serr@lap]-&amp;gt; gpg --homedir /tmp/test-contact --list-keys --keyid-format LONG
/tmp/test-contact/pubring.kbx
-----------------------------
pub   ed25519/38A22EA086864C8F 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
uid                 [ unknown] Primary Key (Clean)
sub   cv25519/5DBD0FD73ACA057F 2026-08-15 [E] [expires: 2027-08-15]
sub   ed25519/7CB4AF1623EBFD0D 2026-08-15 [A] [expires: 2027-08-15]
sub   ed25519/1CC271B607E17A73 2026-08-15 [S] [expires: 2027-08-15]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;т.е. тут сразу со всеми субключами идет (только публичные части)&lt;/p&gt;

&lt;p&gt;проверка что нет приватных ключей&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; echo &quot;test&quot; | gpg --homedir /tmp/test-contact --sign --local-user &quot;Primary Key (Clean)&quot;
gpg: skipped &quot;Primary Key (Clean)&quot;: No secret key
gpg: signing failed: No secret key
~
[serr@lap]-&amp;gt; echo &quot;test&quot; | gpg --homedir /tmp/test-contact --sign --local-user 5DBD0FD73ACA057F
gpg: skipped &quot;5DBD0FD73ACA057F&quot;: No secret key
gpg: signing failed: No secret key
~
[serr@lap]-&amp;gt; echo &quot;test&quot; | gpg --homedir /tmp/test-contact --sign --local-user 1CC271B607E17A73
gpg: skipped &quot;1CC271B607E17A73&quot;: No secret key
gpg: signing failed: No secret key
~
[serr@lap]-&amp;gt; echo &quot;test&quot; | gpg --homedir /tmp/test-contact --sign --local-user 7CB4AF1623EBFD0D
gpg: skipped &quot;7CB4AF1623EBFD0D&quot;: No secret key
gpg: signing failed: No secret key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/15/pgp-keys</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/15/pgp-keys</guid>
        
        
      </item>
    
      <item>
        <title>Использование PGP ключей для доступа по SSH</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Использование PGP ключей для доступа по SSH&lt;/li&gt;
  &lt;li&gt;TAGS: it, linux, guide, pgp, ssh&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;матчасть&quot;&gt;Матчасть&lt;/h1&gt;

&lt;h2 id=&quot;gpg-agent&quot;&gt;gpg-agent&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;gpg-agent&lt;/strong&gt; — это программа-посредник, которая:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Хранит приватные ключи в памяти&lt;/li&gt;
  &lt;li&gt;Запрашивает пароль, когда нужно использовать ключ&lt;/li&gt;
  &lt;li&gt;Выполняет операции (подпись, расшифровка) не отдавая ключ наружу&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Простыми словами: ваши секретные ключи лежат на диске
зашифрованными. Когда приложению нужно что-то подписать или
расшифровать, оно не трогает файлы напрямую, а просит gpg-agent
сделать это. Агент сам расшифровывает ключ (спросив пароль), выполняет
операцию и отдаёт результат&lt;/p&gt;

&lt;p&gt;Обычно SSH работает через ssh-agent, который хранит обычные SSH-ключи&lt;/p&gt;

&lt;p&gt;Но вы хотите использовать GPG-ключ для SSH. Значит:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SSH-клиент должен общаться не с ssh-agent, а с gpg-agent&lt;/li&gt;
  &lt;li&gt;gpg-agent должен уметь говорить по протоколу SSH&lt;/li&gt;
  &lt;li&gt;gpg-agent должен знать, какой GPG-субключ использовать для SSH&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;настройка&quot;&gt;Настройка&lt;/h1&gt;

&lt;h2 id=&quot;настройка-конфига-агента&quot;&gt;Настройка конфига агента&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;enable-ssh-support&quot; &amp;gt; ~/.gnupg/gpg-agent.conf
echo &quot;pinentry-program /usr/bin/pinentry-curses&quot; &amp;gt;&amp;gt; ~/.gnupg/gpg-agent.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gnupg/gpg-agent.conf&lt;/code&gt; — файл настроек gpg-agent&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pinentry-program /usr/bin/pinentry-curses&lt;/code&gt; говорит gpg-agent, какую
программу использовать для ввода пароля&lt;/li&gt;
  &lt;li&gt;Строка &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable-ssh-support&lt;/code&gt; говорит агенту: «Создай ещё один канал
(сокет), по которому SSH-клиент сможет с тобой общаться»&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;keygrip-ssh-субключа&quot;&gt;keygrip SSH-субключа&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Keygrip&lt;/strong&gt; — это внутренний идентификатор, который gpg-agent
использует для хранения и поиска ключей&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; gpg --list-keys --with-keygrip --with-subkey-fingerprints 38A22EA086864C8F
pub   ed25519 2026-08-15 [SC]
      0C73E648FEEA465E8F6493AF38A22EA086864C8F
      Keygrip = 35AA36C80511FB0EA34C5FC183C30DB84629B3DD
uid           [ unknown] Primary Key (Clean)
sub   cv25519 2026-08-15 [E] [expires: 2027-08-15]
      9A819283B120FA7E83523B2E5DBD0FD73ACA057F
      Keygrip = 74E0DCAD28C27CF7DB8D8A5FFA900AC3D6340B43
sub   ed25519 2026-08-15 [A] [expires: 2027-08-15]
      41D2FE1921C303CD746A5B1E7CB4AF1623EBFD0D
      Keygrip = 8C5698905719F63292B051BC743F4DDE8F931C0F
sub   ed25519 2026-08-15 [S] [expires: 2027-08-15]
      A30018F5A2F7499F1474572E1CC271B607E17A73
      Keygrip = BAF88477B915A40928C0C9F8CB45F4E311C40FAE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;для аутентификации этот &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8C5698905719F63292B051BC743F4DDE8F931C0F&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;регистрация-ключа-для-ssh&quot;&gt;Регистрация ключа для SSH&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;8C5698905719F63292B051BC743F4DDE8F931C0F&quot; &amp;gt;&amp;gt; ~/.gnupg/sshcontrol
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gnupg/sshcontrol&lt;/code&gt; — файл, в котором перечислены ключи,
разрешённые для SSH&lt;/li&gt;
  &lt;li&gt;Добавляя сюда keygrip, вы говорите gpg-agent: «Этот ключ можно
использовать, когда SSH попросит подписать что-то»&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;перезапуск-gpg-agent&quot;&gt;Перезапуск gpg-agent&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpgconf --kill gpg-agent
gpgconf --launch gpg-agent
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--kill&lt;/code&gt; — остановить текущего агента&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--launch&lt;/code&gt; — запустить нового, чтобы он прочитал gpg-agent.conf и
sshcontrol&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;указать-ssh-клиенту-где-искать-ключи&quot;&gt;Указать SSH-клиенту, где искать ключи&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpgconf --list-dirs agent-ssh-socket&lt;/code&gt; — спрашивает: «Где gpg-agent
создал SSH-сокет?» Получаем путь, например
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/run/user/1000/gnupg/S.gpg-agent.ssh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Без этой строки SSH по умолчанию пойдёт в стандартный ssh-agent и не
увидит GPG-ключи&lt;/p&gt;

&lt;h2 id=&quot;проверка-что-ключ-виден&quot;&gt;Проверка что ключ виден&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILDuVae20rXvJDEHZUqKjUYJ7837SFL2HG5O2xNa4XUE (none)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Это публичная часть GPG-ключа, экспортированная в SSH-формат&lt;/p&gt;

&lt;h2 id=&quot;обновление-информации-о-терминале&quot;&gt;Обновление информации о терминале&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gpg-connect-agent updatestartuptty /bye &amp;amp;&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;gpg-connect-agent — отправляет команду работающему gpg-agent&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updatestartuptty&lt;/code&gt; — сообщает агенту: «Обнови информацию о текущем
терминале, чтобы я мог показать окно ввода пароля именно здесь»&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bye&lt;/code&gt; — закрывает соединение после выполнения&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Без этой команды gpg-agent не знает, в каком терминале запрашивать
пароль, и SSH-подключение может завершиться ошибкой
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sign_and_send_pubkey: signing failed: agent refused operation&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;добавление-ключа-на-сервере&quot;&gt;Добавление ключа на сервере&lt;/h2&gt;

&lt;p&gt;Для начала по PGP ключу, который поддерживает авторизацию,
генерируется ssh ключ&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; gpg --export-ssh-key 41D2FE1921C303CD746A5B1E7CB4AF1623EBFD0D
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILDuVae20rXvJDEHZUqKjUYJ7837SFL2HG5O2xNa4XUE openpgp:0x23EBFD0D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;на сервере добавляю этот ключ&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;serr@ninja3773:~$ echo &quot;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILDuVae20rXvJDEHZUqKjUYJ7837SFL2HG5O2xNa4XUE openpgp:0x23EBFD0D&quot; &amp;gt;&amp;gt; ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ну и все, далее уже можно подключаться. в первый раз gpg-agent попросит
пассфразу, потом закеширует и уже не будет просить&lt;/p&gt;

&lt;h1 id=&quot;автонастройка-в-при-старте-bash-сессии&quot;&gt;Автонастройка в при старте bash сессии&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; echo &apos;&apos; &amp;gt;&amp;gt; ~/.bashrc
echo &apos;# Использовать gpg-agent вместо ssh-agent&apos; &amp;gt;&amp;gt; ~/.bashrc
echo &apos;export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)&apos; &amp;gt;&amp;gt; ~/.bashrc
echo &apos;gpg-connect-agent updatestartuptty /bye &amp;amp;&amp;gt;/dev/null&apos; &amp;gt;&amp;gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/15/pgp-for-ssh</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/15/pgp-for-ssh</guid>
        
        
      </item>
    
      <item>
        <title>tcp traceroute с конкретным портом</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: tcp traceroute с конкретным портом&lt;/li&gt;
  &lt;li&gt;TAGS: vpn, traceroute, network, ssh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;например бывает что ssh не подключается к серверу хотя пинг к нему
идет, тут вот пример как потрейсить&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo traceroute -T -p 14160 91.239.206.123
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/08/13/tcp-port-traceroute</link>
        <guid isPermaLink="true">https://laserattack.net/2026/08/13/tcp-port-traceroute</guid>
        
        
      </item>
    
      <item>
        <title>Создание загрузочной флешки Linux</title>
        <description>&lt;ul&gt;
  &lt;li&gt;NAME: Создание загрузочной флешки Linux&lt;/li&gt;
  &lt;li&gt;TAGS: guide, it, linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;вот подключил флешку&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda           8:0    1  57.8G  0 disk
└─sda1        8:1    1  57.8G  0 part /run/media/serr/19043_1052_
nvme0n1     259:0    0 476.9G  0 disk
├─nvme0n1p1 259:1    0   512M  0 part /boot/efi
├─nvme0n1p2 259:2    0     2G  0 part [SWAP]
└─nvme0n1p3 259:3    0 474.4G  0 part /var/lib/docker
                                      /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вот она&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sda           8:0    1  57.8G  0 disk
└─sda1        8:1    1  57.8G  0 part /run/media/serr/19043_1052_
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;размонтирую раздел&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo umount /run/media/serr/19043_1052_
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~
[serr@lap]-&amp;gt; lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda           8:0    1  57.8G  0 disk
└─sda1        8:1    1  57.8G  0 part
nvme0n1     259:0    0 476.9G  0 disk
├─nvme0n1p1 259:1    0   512M  0 part /boot/efi
├─nvme0n1p2 259:2    0     2G  0 part [SWAP]
└─nvme0n1p3 259:3    0 474.4G  0 part /var/lib/docker
                                      /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;туда записываю iso&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/Desktop/iso
[serr@lap]-&amp;gt; sudo dd if=gentoo-install-amd64-minimal-20260426T153103Z.iso of=/dev/sda bs=4M status=progress conv=fsync
792723456 bytes (793 MB, 756 MiB) copied, 1 s, 791 MB/s1052526592 bytes (1.1 GB, 1004 MiB) copied, 1.33385 s, 789 MB/s

250+1 records in
250+1 records out
1052526592 bytes (1.1 GB, 1004 MiB) copied, 180.731 s, 5.8 MB/s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;программно извлекаю флешку&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo eject /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate>
        <link>https://laserattack.net/2026/05/02/create-bootable-linux-usb</link>
        <guid isPermaLink="true">https://laserattack.net/2026/05/02/create-bootable-linux-usb</guid>
        
        
      </item>
    
  </channel>
</rss>
