Страницы

Поиск по вопросам

Показаны сообщения с ярлыком файловая-система. Показать все сообщения
Показаны сообщения с ярлыком файловая-система. Показать все сообщения

понедельник, 30 марта 2020 г.

Как сделать рекурсивный поиск по файлам только определенного расширения?

#cpp #winapi #файловая_система


void getFiles(const string& inpath, string outpath)
{
    string mask = inpath + "\\*";
    WIN32_FIND_DATA FindFileData;
    HANDLE hf;
    hf = FindFirstFile(mask.c_str(), &FindFileData);

    ofstream output;
    output.open(outpath + "\\data.txt", ios_base::app);

    if (hf != INVALID_HANDLE_VALUE)
    {
        do
        {
            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if (strcmp(FindFileData.cFileName, ".") == 0 ||
                    strcmp(FindFileData.cFileName, "..") == 0) continue;
                getFiles(inpath + "\\" + FindFileData.cFileName, outpath);
            }
            else
                output << inpath << "\\" << FindFileData.cFileName << endl;

        } while (FindNextFile(hf, &FindFileData) != 0);
        FindClose(hf);
    }

    output.close();
}


Есть такая функция, которая ищет файлы в выбранном каталоге и его подкаталогах, а
потом записывает пути к файлам в data.txt. А как мне сделать так, чтобы эта функция
искала только файлы с определенным расширением, например, .xml?
    


Ответы

Ответ 1



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

понедельник, 24 февраля 2020 г.

Исключение при вызове метода MoveTo у экземпляра DirectoryInfo

#c_sharp #исключения #файловая_система #io




DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory());
string s = @"C:\";
dir.MoveTo(s);


Текст в s для примера. Какой бы путь не вставил, исключение всё равно вылетает.

Исключение:


  "Процесс не может получить доступ к файлу, так как этот файл занят другим процессом."



Что не так? Вроде простейший код.



UPD 24.03.16. 17.45.

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


Ответы

Ответ 1



Из текущей папки запущенно приложение, вот и не удается переместить её. По крайней мере - это то что приходит в голову в первую очередь.

пятница, 31 января 2020 г.

Автоматическое включение Android телефона при подключении зарядки

#android #файловая_система #system #root


Всем доброго времени суток. Есть телефон, из которого я хотел бы сделать видео-регистратор
для моего автомобиля. Програмку то написал, которая будет видео записывать, сохранять
и т.д. Но как сделать так, чтобы телефон включался при подключении USB-зарядки, провод
которой вставлен в розетку 220B?

ROOT доступ полный. Модель телефона - ZTE Blade Q Lux 3G PRO. Android 4.4.2.

В интернете читал статьи как это сделать на телефонах Samsung. Как сказано тут в
одном из ответов, необходимо изменить содержимое файлов /system/bin/playlpm или /system/bin/lpm,
но ни того, ни другого файла в моём телефоне не существует. Насколько я понял принцип,
надо найти файл, который запускает анимацию батарейки



и добавить строчки включения системы:


  #!/system/bin/sh
  
  /system/bin/reboot


Пробовал менять всё содержимое на эти строки в следующих файлах:


/system/bin/bootanimation
/system/bin/ipod
/system/bin/chcon


Безрезультатно. Может какие-то другие файлы надо изменить? Или вообще в другой папке
копаться надо? Подскажите.



Когда попробовал проделать такой трюк с файлом /system/bin/chcon у меня перестала
работать кнопка отключения дисплея. То есть, короче, как я включил телефон, так дисплей
и работает, не отключается и не реагирует на кнопку выключения. Только после восстановления
прежнего файла всё стало нормально после перезагрузки телефона. Так что делаю вывод,
что автоматическое включение реализовать возможно. Каждый системный файл действительно
запускает скрипты, каждый свой, и отвечает каждый сам за себя. Необходимо лишь понять
где нужный файл анимации заряда батареи телефона и где в нём именно надо вписать команду
включения.
    


Ответы

Ответ 1



Для того, чтобы телефон запускался при подключении зарядки необходимо выполнить следующие действия. Скачать и установить на компьютер программу Adb Run. Она необходима для установки соединения с телефоном по USB-проводу. Скачать и установить на компьютер программу Bootimg. Она необходима для редактирования файла boot.img прошивки телефона. Патч программы должен быть C:\Users\Admin\. Скачать прошивку (ZIP-архив) на компьютер. Из архива достать файл boot.img и положить в папку с программой Bootimg. Открыть системную консоль (Меню пуск -> cmd). Ввести следующую команду: bootimg --unpack-bootimg Открыть создавшуюся папку initrd, найти в ней файлы init.rc и init.charging.rc. Открыть их. В каждом из них находим строки: service ipod /system/bin/ipod user root group root и после них ниже пишем: class_start core class_start main class_start default Сохраняем и закрываем оба файла. Возвращаемся в консоль. Пишем следующую команду: bootimg --repack-bootimg Запускаем программу Adb Run. По умолчанию она устанавливается в C:\adb\. Выбираем пункт 5, затем 0. Телефон должен войти в режим Fastboot. Затем выбираем пункт 1, и затем 1. В консоли должны отобразится как минимум две строки: List of devices attached 0123456789ABCDEF device Если вы видите только это: List of devices attached то у Вас могут быть следующие ошибки: Не включена отладка USB в режиме разработчика Некорректно подключён USB-провод или, возможно, повреждён В диспетчере устройств (Правой кнопкой мыши по ярлыку "Мой компьютер"; Свойства; Диспетчер устройств) телефон отображается как неизвестное устройство, например, как на скриншоте здесь Затем выбираем пункт 5, затем 0. Ждём, когда на экране телефона появится надпись FASTBOOT mode.. или что-то наподобие. Выбираем пункт 7, затем 1, и затем пишем: fastboot flash boot C:\Users\Admin\boot-new.img Ждём, когда в консоли появится надпись Done... в конце в случае успешной установки boot.img. Затем опять пишем в консоли: fastboot reboot Закрываем программу Adb Run (консоль). Отключаем телефон от компьютера. После загрузки телефона выключаем его. Подключаем телефон к зарядке 220В, и, в течение 10 секунд телефон сам включится. На этом всё готово. Если у Вас произошли какие-то ошибки или что-то пошло не так, я прошу написать в комментариях. Буду рад помочь! Я хочу ещё дать людям совет: Если с первой попытки выполнить процедуру не получилось по какой-либо причине, попробуйте ещё несколько раз внимательно выполнить все выше перечисленные действия. Если версия Android - 4.4 и выше, то могут возникнуть ошибки и, возможно, ничего не получится. Как мне, например, ответила поддержка корпорации ZTE на вопрос "возможно ли хоть как-то реализовать автовключение телефона при..." мне ответили, что видите ли на данном лаунчере такая функция недоступна. И я понял, что решить проблему можно лишь откатом версии Android с 4.4 на 4.2.

Ответ 2



Если я правильно помню, такая фишка зависит от загрузчика, типа TWRP. Но в старых версиях при подключении usb-зарядки, загрузка начинается и заканчивается только на самом загрузчике. Чтобы запустить далее андройд, нужно вручную выбрать пункт system reboot. Покопайте в этом направлении.

вторник, 28 января 2020 г.

Копирование нескольких файлов через командную строку в windows

#windows #cmd #файловая_система


На linux скопировать, скажем два файла, можно с помощью команды cp просто написав
эти файлы через пробел и прописав путь куда эти они копируются. 
Пример: cp file1.txt index.html C:\new_folder

А как это сделать в cmd windows?
Команда copy копирует один файл, а два файла записанные через пробел вызывают ошибку.

Как это можно сделать? Как должна выглядеть такая команда?
    


Ответы

Ответ 1



Вы можете воспользоваться командой robocopy. Больше примеров есть тут. Пример использования: robocopy source\folder dest\folder file1.txt index.html

Ответ 2



В Windows есть PowerShell -- оболочка с интерфейсом командной строки и сопутствующего языка сценариев. Копировать файл можно так Copy-Item c:\scripts\test.txt c:\test Копировать папку с файлами и всеми подпапками так Copy-Item c:\scripts c:\test -recurse у Copy-Item есть alias'ы: cpi, cp, copy Описание тут.

Ответ 3



В 2016 году для запуска UNIX-команд и утилит в Windows рекомендуют воспользоваться Cash. Быстрая установка, Команды точно такие же, как и на UNIX-подобных ОС, не нужно переучиваться, Возможность комбинирования команд различных операционных систем. Устанавливаем Node.js, если ещё нет, → в командной строке Windows вводим npm install cash-global -g → ждём, пока установится, → тестируем команды для Linux прямо там же, в Cmd.exe. Список поддерживаемых Cash команд, небольшой обзор Cash на русском языке. Чтобы применять команды alias, clear, export, false, kill, source, true и unalias на август 2016 потребуется предварительно вводить cash, пример: E:\Киролайна>alias sl=ls "alias" не является внутренней или внешней командой, исполняемой программой или пакетным файлом. E:\Киролайна>cash SashaGoddess@DESKTOP-0G54NVG:E/Киролайна$ alias sl=ls SashaGoddess@DESKTOP-0G54NVG:E/Киролайна$ Все остальные поддерживаемые команды работают и без cash. Для копирования нескольких файлов после установки Cash достаточно ввести в интерпретатор командной строки то же самое: cp file1.txt index.html C:\new_folder.

четверг, 23 января 2020 г.

Как получить сектора, на которых расположен файл?

#c_sharp #файлы #файловая_система #ntfs #fat32


В общем нужно получить сектора, на которых расположен файл и перезаписать их. Каким
образом это можно осуществить на C#? Может кто-то сталкивался?
    


Ответы

Ответ 1



Прямой доступ к секторам запоминающего устройства в C# можно осуществить следующим образом (но начиная с Windows Vista этот код подойдёт только для чтения): using System; using System.IO; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace ConsoleApplication { class Program { const uint OPEN_EXISTING = 3; const uint GENERIC_READ = 0x80000000; const uint GENERIC_WRITE = 0x40000000; [DllImport("kernel32", SetLastError = true)] static extern SafeFileHandle CreateFile ( string FileName, uint DesiredAccess, uint ShareMode, uint SecurityAttributes, uint CreationDisposition, uint FlagsAndAttributes, int hTemplateFile ); static void Main() { byte[] buffer = new byte[512]; // PhysicalDrive2 - имя целевого привода using (SafeFileHandle handle = CreateFile("\\\\.\\PhysicalDrive2", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)) { using (FileStream stream = new FileStream(handle, FileAccess.ReadWrite)) { int sector = 35792; // Read stream.Position = 512 * sector; stream.Read(buffer, 0, 512); // Write (начиная с Windows Vista требует дополнительных манипуляций) stream.Position = 512 * sector; stream.Write(buffer, 0, 512); } } for (int i = 0; i < buffer.Length; i++) Console.Write(buffer[i].ToString() + ' '); Console.ReadKey(); } } } Не смотря на то, что позиция в данном случае указывается в байтах, так же, как это происходит при работе с памятью через драйвер файловой системы, обращение будет произведено именно к тому сектору, в котором расположен указанный байт. То есть в случае, если stream.Position = 600, чтение будет произведено с самого начала первого сектора — так, как будто stream.Position = 512, то есть с начала ячейки памяти. Особенности работы в ОС начиная с Windows Vista Для того, чтобы получить объект SafeFileHandle, запускать программу нужно с правами администратора. Без дополнительных манипуляций доступны для перезаписи: загрузочный сектор логического раздела BPB (англ. BIOS parameter block), однако его резервная копия без дополнительных манипуляций не доступна, равно как и все остальные сектора логического раздела (Condition 1); сектора, не принадлежащие ни одному логическому разделу, например сектор с MBR (англ. master boot record) и зарезервированное за ним пространство, или сектора, не отнесённые ни к одному логическому разделу по другим причинам (Condition 2). Условия доступа к секторам, принадлежащим логическим разделам: неявная блокировка раздела, посредством запроса эксклюзивного доступа для записи (Condition 3); явная блокировка раздела, посредством отправки запроса на блокировку и запроса на отключение (Condition 4); запрос на запись с флагом SL_FORCE_DIRECT_WRITE (Condition 5). To be continued... FAT32 Список секторов, на которых расположен файл, вычисляется из его кластерной цепочки. Начало кластерной цепочки хранится в записи о файле в директории, в которой он лежит. To be continued...

среда, 22 января 2020 г.

C++ Определение физического расположения файла на диске

#cpp #winapi #файловая_система


Для начала, мне не даётся использование DeviceIoControl.
Говорит, что hFile неверный, хотя он не NULL. С какими атрибутами открывать?

ANDLE hFile = CreateFile(path,
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    0,
    NULL);

RETRIEVAL_POINTERS_BUFFER rpb;
STARTING_VCN_INPUT_BUFFER startingVcn;
DWORD dwBytesReturned;
OVERLAPPED overlapped;

DWORD dwFragments = 0, dwTotalClusters = 0;

    // ...

    DeviceIoControl(hFile, 
        FSCTL_GET_REPARSE_POINT,
        &startingVcn, sizeof startingVcn,
        &rpb, sizeof rpb,
        &dwBytesReturned, &overlapped);

     // ... while(dwError == ERROR_MORE_DATA);


Далее алгоритм будет следующий:
Вызвать GetLastError и по коду ошибки определить дальнейшие действия.


Если это первая итерация и ERROR_HANDLE_EOF, значит файл резидентный, если не первая,
то выход из цикла.
Если NO_ERROR, файл занимает 1 фрагмент кластеров (т.е. все по-порядку, а не в разброс
по диску).
Если ERROR_MORE_DATA, есть ещё на диске фрагменты этого файла.


Таким образом, будем иметь список всех фрагментов файла на диске ввиде списка пар
StartLCN-ClustersLength.

Так же, понятия не имею, что делать, если файл резидентный. Как вытащить его положение
на диске?

Работать всё это должно под FAT, FAT32 и NTFS (с поддержкой SPARSE, COMPRESSED и
ENCRYPTED файлов).

Нужно это положение для программы безопасного удаления данных, т.е. потом я полученные
кластеры затру.
    


Ответы

Ответ 1



HANDLE hFile = CreateFile(lpFilePath, FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (hFile == INVALID_HANDLE_VALUE) { wcout << "Open file error: " << GetLastError() << endl; system("pause"); return -1; } PSTARTING_VCN_INPUT_BUFFER startingVcn = new STARTING_VCN_INPUT_BUFFER(); PRETRIEVAL_POINTERS_BUFFER prpb = new RETRIEVAL_POINTERS_BUFFER(); DWORD dwBytesReturned = 0; DWORD dwExtentNumber = 0, dwTotalClusters = 0; DWORD dwError; do { DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, startingVcn, sizeof *startingVcn, prpb, sizeof *prpb, &dwBytesReturned, NULL); dwError = GetLastError(); switch (dwError) { case ERROR_HANDLE_EOF: if (dwExtentNumber == 0) cout << "MFT Table..." << endl; else wcout << endl << "Total clusters: " << dwTotalClusters << endl << "Fragments: " << dwExtentNumber << endl; break; case NO_ERROR: wcout << "Fragment 0:" << endl << "\tStart LCN: " << prpb->Extents->Lcn.QuadPart << endl << "\tClusters: " << prpb->Extents->NextVcn.QuadPart - prpb->StartingVcn.QuadPart << endl << endl; wcout << "Total clusters: " << prpb->Extents->NextVcn.QuadPart - prpb->StartingVcn.QuadPart << endl << "Fragments: " << dwExtentNumber; break; case ERROR_MORE_DATA: wcout << "Fragment " << dwExtentNumber << ":" << endl << "\tStart LCN: " << prpb->Extents->Lcn.QuadPart << endl << "\tClusters: " << prpb->Extents->NextVcn.QuadPart - prpb->StartingVcn.QuadPart << endl << endl; dwTotalClusters += prpb->Extents->NextVcn.QuadPart - prpb->StartingVcn.QuadPart; startingVcn->StartingVcn.QuadPart = prpb->Extents->NextVcn.QuadPart; break; default: wcout << "DeviceIoControl error: " << dwError << endl; system("pause"); return -1; } dwExtentNumber++; } while (dwError == ERROR_MORE_DATA); CloseHandle(hFile); system("pause"); return 0;

среда, 1 января 2020 г.

Где хранить исходные файлы / задания проекта?

#файловая_система #дерево #проект


Интересует правильное именование папок или файловой структуры (если такая существует).

В самом начале разработки мне передают файлы для верстки, ТЗ и т.д.
Где правильно хранить исходные файлы проекта?
Например, такие как:


design.psd
design.jpg
ТЗ.doc


и т.п.

Естественно, файлов может быть больше / меньше и их названия могу варьироваться.
Основной смысл заключается в правильности их размещения в проекте или вне проекта.

Push-ить ли их в репозиторий вместе с исходными кодами или нет?

Изначальная файловая структура была следующей:  


  .git
  .idea - именно сюда я и складывал изначальные файлы, пока не прочитал, что это
директория для IDE, в связи с чем и возник данный вопрос.
  src
  package.json
  ...

    


Ответы

Ответ 1



Ваш вопрос: Интересует правильное именование папок или файловой структуры (если такая существует). Ответ: Могу лишь поделится личным опытом структурирования файлов на фронтенде. На вопрос, как правильно, который я задал с десятку знакомых (когда у меня он встал на повестке дня), мне отвечали по разному, но общая параллель была. В основном это две основные папки (названия каждый называл разные, но суть одна и та же) + папки node_modules, bower_components. Эта структура обычного фронтенд проекта (хотя, как написал выше, все это опционально). Получается структура вроде этой: |-/build |-/node_modules |-/bower_components |-/src |--- doc/ |--- fonts/ |--- img/ |--- scripts/ |--- style/ |--- vendors/ |--- template/ |--- index.html |- .gitignore |- gulpfile.js |- package.json |- bower.json |- README.md /built - папка продакшена (многие ее именуют /dist). /src - папка исходников (многие ее именуют /app). Также еще может быть отдельная папка для тасков. Папка /src тоже может структурироватся иначе, не все прям в нее на кучу (все зависит от объема), а подразделять ее на папки типа: /assets,/template и т.д. Ваш вопрос: В самом начале разработки мне передают файлы для верстки, ТЗ и т.д. Где правильно хранить исходные файлы проекта? Ответ: Я храню в /template, мне кажется это логичным. Также рекомендую нормально структурировать .gitignore - комментируйте, что там откуда и куда, вам же легче будет. Ваш вопрос: Push-ить ли их в репозиторий вместе с исходными кодами или нет? Ответ: Я не делаю для них push, нет необходимости.

Ответ 2



Нашел ответы по запросу: структура проектной папки И как осознание, что единой структуры не существует и под каждый проект своя структура. Решил взять на вооружение некоторые советы отсюда: https://habrahabr.ru/post/319296/ Создал рядом с папкой .git папку .incoming В которой уже все структурирую в зависимости от проекта.

понедельник, 30 декабря 2019 г.

Отличие файлов SharedPreferences от обычных файлов

#android #файловая_система #android_shared_preferences


Определение SharedPreferences из документации:


  Объект SharedPreferences указывает на файл, содержащий пары
  "ключ-значение", и предоставляет простые методы для чтения и записи.
  Управление каждым файлом SharedPreferences осуществляется с помощью
  инфраструктуры и может быть частным или общим.


А чем отличаются файлы, используемые SharedPreferences, от обычных файлов в файловой
системе Android? Одно из отличий, как написано в той же документации, если я правильно
понимаю, состоит в строго определённой структуре файла (набор пар "ключ-значение").
А есть ли ещё отличия? Например, эти файлы можно прочитать только программно (т.е.
можно прочитать только из приложения/приложений, которые обращаются к этим SharedPreferences,
а открыть из файловой системы их нельзя) или же это неверно?
    


Ответы

Ответ 1



Файлы SharedPreferences это обычные файлы с расширением .xml которые располагаются в подкаталоге конкретного приложения. При этом права доступа к файлу прописываются такие же как и всем остальным, которые принадлежат данному приложению. Соответственно получить доступ к нему можно не только программно, но и из файловой системы. Другим программам на устройстве получить доступ к данному файлу не удастся,так как они не имеют соответствующих прав, за исключением тех программ которые имеют права root или же подписаны тем же сертификатом, что и программа которой принадлежит SharedPreferences

вторник, 24 декабря 2019 г.

C++ Корневая (текущая) папка программы (указать)

#cpp #windows #visual_studio #файлы #файловая_система


С++ Как задать корневую (текущую) папку для программы, 
например у меня есть параграмма с файлами картинками (ресурсы программы), но если
я откраиваю файл с помощью этой параграммы, программа не находит своих файлов (ресурсов).
в итоге
Корневая (текущая) директория устанавливается в папке с открытым файлом. 
А если я просто открываю программу Корневая (текущая) директория устанавливается
в папке с программой, и все ресурсы загружаются отлично. 

Использую visual studio 2010
ОС: windows 10 x86

Ответ что я ожыдал
функцыя устанавлевает корневую папку SetCurrentDirectory("Путь к корневой папке програмы");
    


Ответы

Ответ 1



Рабочий каталог используется для поиска файлов, заданных с помощью относительного пути. Текущая директория наследуется программой от её родителя. К примеру, если запустить в командной строке, то по умолчанию программа будет искать файлы относительно текущего пути в командной строчке (pwd, echo %cd%). Если вы запускаете программу вне папки с картинками, то в настройках передайте необходимый путь (в конфигурационном файле, в переменных окружения, в опциях командной строки) и используйте абсолютные пути для открытия файлов (переданный путь + относительный путь). Вот пример, где используется либо путь, заданный в командной строке, либо текущий путь, если не задан путь: fs::path dirpath = (argc == 2) ? argv[1] : fs::current_path(); Чтобы объединить переданный путь и относительный путь: path = dirpath / relpath. Выражение "корневая директория" не имеет отношения к текущей директории (к примеру, chroot(2) vs. chdir(2)).

Ответ 2



вот пришёл к такому решению благодаря ан пользователю jfs сумел правильно сформулировать поисковый запрос еще такой парадокс на заметку: когда я в настройках проекта -> компонировщик - дополнительно устанавливаю точку входа "main" аргумент argc ведет себя странно и принимает некоректное значение, и и массив argv становится пустым что приводит к ошибке при запуске. проект создаётся как пустой; функция SetCurrentDirectory("путь"); устанавливает текучкою директорию. #include #include using namespace std; void main(int argc, char*argv[]) { //Получить текущую деректорию //TCHAR buffer[MAX_PATH]; //GetCurrentDirectory(sizeof(buffer),buffer); //cout << buffer << endl; if(argc > 0) { //получаим путь к програме string urlK = ""; urlK += argv[0]; //удаляем всё до слеша (название програмы) while(urlK.size() > 0) { bool br = 0;if(urlK[urlK.size()-1] == '/' || urlK[urlK.size()-1] == '\\'){br = 1;} urlK.erase(urlK.size()-1); if(br)break; } //устанавливаем путь к кааталогу с прорамой как основной SetCurrentDirectory(urlK.c_str()); } return; } Вот пример где устранена ошибка которою я описывал выше. #include #include //======================= //Отримати місцезнаходження програми char* SvcInstall() { TCHAR szPath[MAX_PATH]; if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) { return "error"; } return szPath; } //Отримати місцезнаходження програми і задати папку як кореневу std::string SetDIR() { //получаим путь к програме std::string urlK = ""; urlK += SvcInstall(); //удаляем всё до слеша (название програмы) while(urlK.size() > 0) { bool br = 0;if(urlK[urlK.size()-1] == '/' || urlK[urlK.size()-1] == '\\'){br = 1;} urlK.erase(urlK.size()-1); if(br)break; } //устанавливаем путь к кааталогу с прорамой как основной SetCurrentDirectory(urlK.c_str()); return urlK; } int main() { // функцыя возвращает путь к папке с програмой, и автоматичиски устанавлевает ее ка текущую SetDIR(); return 0; }

воскресенье, 22 декабря 2019 г.

Когда осуществляется запись на диск без close/fsync?

#c #linux #unix #файловая_система


Допустим, есть программа, которая вызвала write(). Затем ушла в вечный цикл без вызова
close() или fsync()/sync(). В каком случае произойдет реальная запись данных на диск?
Файловая система без барьеров, паралельные процессы sync() не вызывают.
    


Ответы

Ответ 1



Информация из write() попадает в "грязные" страницы кэша (Dirty). Через некоторое время (стандартно каждые 5 секунд = /proc/sys/vm/dirty_writeback_centisecs / 100) ядро проверяет не пора ли что записывать из грязных страниц. Ядро записывает страницы прождавшие более /proc/sys/vm/dirty_expire_centiseconds времени (стандартно 30 секунд) либо раньше, если осталось мало свободной памяти. Точнее второе условие задаётся в виде: Dirty * 100 / (MemFree + Cached - Mapped) > /proc/sys/vm/dirty_background_ratio либо Dirty > /proc/sys/vm/dirty_background_bytes в зависимости от того какой параметр установлен. Кроме этого предусмотрен ещё крайний случай: когда грязный кэш разрастается больше /proc/sys/vm/dirty_ratio процентов от доступной памяти (MemFree + Cached - Mapped) либо больше /proc/sys/vm/dirty_bytes, то все процессы выполняющие запись приостанавливаются до фактической записи их данных (блокирующая запись). Источники: https://www.kernel.org/doc/Documentation/sysctl/vm.txt http://www.westnet.com/~gsmith/content/linux-pdflush.htm

пятница, 20 декабря 2019 г.

Как создать папку средствами c++

#cpp #c #файловая_система


Мне нужно написать программу, которая будет сохранять файлы следующим образом.

Проверить, существует ли папка "1" и если нет, то создать папку "1" и затем положить
туда картинку test_1, проверить существует ли папка "N", если нет, то создать и положить
туда картинку test_N. 
    


Ответы

Ответ 1



Начнем с простого пути :) stat из для проверки существования файла/каталога. mkdir для создания каталога. Откровенно говоря, буду удивлен, если какой-то компилятор их не имеет. Но если даже это рассматривается как измена чистому C++ - то в C++17 реализована масса функциональности для работы с файлами. Проверка существования - с помощью exists, создание каталога - create_directory.

суббота, 14 декабря 2019 г.

Копирование большого количества файлов с диска на удаленную машину в Linux

#linux #файлы #файловая_система #резервное_копирование #передача_данных


суть вопроса такова:

Имеется диск на хосте А, размером 3.7 Тб, на который записано 1.3 Тб данных на разделе
NTFS.

Имеется удаленный хост В, на котором есть 2 Тб свободного места на разделе ext4

Задача: скопировать с хоста А на хост В 1.3 Тб данных.

Почему я не использую rsync? Потому что при копировании мелких файлов скорость передачи
падает до 100 кбайт/с, а таковых там достаточно много

Почему не dd? Потому что копируется весь раздел, размером 3.7 Тб.

Подскажите, как правильно и относительно быстро скопировать данные на удаленный хост?
Возможно, можно использовать dd, но не создавать образ размера 3.7 тб?

ОС на хосте А: Ubuntu 18.04

На хосте В: Debian 8
    


Ответы

Ответ 1



Обычно самый простой способ — это запаковать файлы в архив и отправить на удалённый хост. Можно это сделать обычным tar'ом tar cf /path/to/my/tarfile.tar.gz -C /путь/к/источнику . scp /path/to/my/tarfile.tar.gz host_В:/tmp/ ssh host_В tar xf /tmp/tarfile.tar.gz -C /путь/к/назначению Но т.к. на удалённом хосте, по условию задачи, не хватит места и для архива и для данных, то можно воспользоваться тем, что утилита tar умеет читать и писать в/из стандартного ввода/вывода и сделать всё в одно действие: tar cz -C /путь/к/источнику . | ssh host_В tar xz -C /путь/к/назначению Если в ssh включена компрессия или канал достаточно широкий, то следует убрать ключ z из аргументов tar. Из минусов — отсутствие возможности возобновить передачу при сбое.

Хранение файлов в базе vs хранение в файловой системе

#sql #windows #sql_server #файлы #файловая_система


Хотелось бы увидеть + и - различных видов хранения, и когда какой лучше использовать.

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

UPD:
Enttity Framework дружит с файловыми таблицами?
    


Ответы

Ответ 1



В SqlServer вы можете использовать следующие варианты (некоторые из них применимы и к другим СУБД). Вариант 1 В БД хранится "заголовок" файла (например, путь к файлу плюс, возможно, какой-то набор атрибутов): create table [TableName] ( ... FilePath nvarchar(4000) not NULL, ... ) а данные хранятся отдельно в файловой системе. Размер БД меньше, чем если хранить в БД также и данные. Но нужно следить за ситуациями "файл есть, заголовка нет" или "заголовок есть, файла нет". На мой взгляд, если файлы являются логически важной частью данных БД (не кэш, не какие-то временные данные), то лучше посмотреть на другие варианты. Вариант 2 В БД хранится также и содержимое файла (в столбце типа varbinary(max)). create table [TableName] ( ... FileData varbinary(max) FILESTREAM not NULL, --либо --FileData varbinary(max) not NULL, ... ) Здесь две опции - с FILESTREAM и без. Без FILESTREAM: данные хранятся в БД (в т.н. LOB pages) размер данных одного элемента ограничен 2Gb С FILESTREAM: данные хранятся в файловой системе (именно как файлы) нет ограничения в 2Gb на элемент данные FILESTREAM не участвуют при подсчёте лимита на макс. размер БД (к чему чувствительны Express Edition) к данным можно получить доступ через соотв. API со стороны файловой системы (SqlServer 2014 и далее) запрашиваемые данные не отъедают из buffer pool, оставляя больше памяти для обработки запросов И с FILESTREAM и без поддерживаются транзакции. С FILESTREAM при доступе через Transact-SQL поддержка полная, при доступе через файловую систему есть ограничения (смотреть здесь). Вариант 3 Использование таблиц специального типа FileTable. create table [FileTableName] as filetable Их функционал основан на использовании FILESTREAM. Таблица представляет иерархию хранящихся файлов/директорий, их данные и атрибуты. В варианте 2, чтобы создать/удалить файл, нужно создать/удалить соотв. запись в таблице. В данном варианте это можно делать напрямую через файловую систему. Например зайти в соответствующую директорию (SqlServer создаёт для этого соответствующую UNC share), создать какой-то файл/директорию, удалить/изменить, потом сделать запрос select * from FileTableName и увидеть соответствующие изменения. И наоборот - при вставке записи в таблицу через SQL в директории появится соответствующий файл или директория. Какой вариант когда лучше использовать - думаю, зависит от конкретной задачи. В документации более детальное описание и сравнение вариантов 2 и 3.

C#: Файловая транзакция

#c_sharp #net #windows #файловая_система #файлы


Существуют ли в C# средства, которые позволяют делать файловые транзакции?

Например, при копировании файлов вылетает ошибка и все откатывается.
    


Ответы

Ответ 1



Родных средств для этого в C# нет, т.к. для полноценной реализации файловых транзакций, необходимо поддержка на уровне файловой системы и операционной системы. В Microsoft пробовали сделать что-то подобное, однако проект не взлетел. Теоретически, можно попробовать задействовать для подобных целей какую-нибудь систему резервного копирования или реализовать функционал резервирования и восстановления состояния директории самостоятельно. Предполагаемый порядок действий в таком случае: Создаем копию состояния изменяемой директории в архив. Производим необходимые изменения. Если произошла ошибка - откат, восстанавливаем состояние директории из архива. Удаляем архив. Предполагаемые проблемы: Что делать, если возникла ошибка при создании архива для отката? Считаем транзакцию не начатой? Выполняем изменения с риском испортить данные без возможности отката? Что делать, если после ошибки при изменении, возникла ошибка при откате? Что делать, если не достаточно дискового пространства для выполнения всех указанных действий, т.к. при создании архива для отката мы удваиваем используемый объем диска. Да, некоторые файлы хорошо сжимаются, но далеко не все. Плюс, используя сжатие, мы существенно увеличим время создания архива, и выполнение отката, если в нем возникнет необходимость, и добавим еще одно потенциально проблемное место в алгоритм. Не стоит забывать про права доступа к файлам и каталогам. Но это уже отдельная история, тоже довольно запутанная и не имеющая простого решения в общем случае. UPD: По мотивам комментария VladD: Если допустимо в рамках решаемой задачи, можно принять следующее соглашение: По аналогии с базами данных, есть некая рабочая директория для которой реализуется механизм транзакций. В этой дирректории заведомо все правильно, права доступа есть на все операции и т.д. Это можно обеспечить например с помощью инсталятора программы. Любое вмешательство в настройки директории - целенаправленная диверсия. Тогда часть описанных и не описанных выше проблем устраняется, однако остается проблема "электрик Вася", который не вовремя выключил электричество, но попробуем обойти и ее. Рассуждения чисто теоретические, на практике не реализовывал, о недостатках пишите в комментарии. Создаем новую папку со служебным именем. Все операции в рамках транзакции производим в ней. В эту папку первым шагом кладем текстовый файл с планом транзакции, простой список необходимых операций или готовых для выполнения команд. Это позволит проверить что уже сделано, и возобновить работу после вмешательства "электрика Васи". Выполняем транзакцию согласно плану. Если все успешно, переименовываем созданную папку в ChangedDirName-New и перемещаем ее в нужное место. Перемещение в рамках одного физического диска выполняется мгновенно. Переименовываем папку которую должны были менять в рамках транзакции во что-то типа ChangedDirName-Old. Это позволит вернуть все в исходное состояние простым переименованием. Если все успешно, переименовываем ChangedDirName-New в оригинальный ChangedDirName. Если все успешно, удаляем (или архивируем на память) ChangedDirName-Old и удаляем из ChangedDirName план транзакции. Если что-то пошло не так: ошибка при создании папки транзакции - аварийно завершаем транзакцию исключением. ошибка при создании плана транзакции - аварийно завершаем транзакцию исключением, и удаляем папку транзакции. ошибка при выполнении операций транзакции - аварийно завершаем транзакцию исключением, и удаляем папку транзакции. непредвиденное прерывание транзакции (пришел "электрик Вася") - сверяем состояние папки транзакции с планом и продолжаем начатое. Плюсы: изменяемая папка доступна другим частям программы все время выполнения транзакции в неизменном виде. замена папки описанным способом происходит с максимальной, для целевой файловой системы, скоростью. восстановление после сбоя, если вдруг "электрик Вася" окажется снайпером и выключит ток между переименованиями, выполняется простым переименованием, и может быть автоматизировано, если структура папок постоянна. Минусы: много места, и сжатие здесь не спасет в виду отсутствия такового. абсолютно уверен что есть еще не мало, но оставлю на суд критиков.\ По желанию сообщества, первый предложенный вариант может быть удален или оставлен для истории. "электрик Вася" является вымышленным персонажем, все совпадения случайны! =) UPD2 Вспомнил о способе сформировать новую папку из существующих файлов не удваивая их количество. В *NIX системах существует штуковина, которая называется HardLink. Ведет она себя так же, как активная ссылка на объект в .NET, пока есть хоть одна, файл не будет удален. Когда-то давно, обнаружил, что такое есть и в NTFS, только спрятано от простых пользователей. На EnSO практически сразу нашлось решение для создания HardLink-ов. Подробнее тут. Таким образом можно немного изменить предложенные варианты, и заменить реальное копирование файлов, на создание HardLink-ов в папке транзакции, тем самым исключая излишний расход ресурсов, необходимых для копирования. Но, как всегда не без ложки дегтя, этот метод применим только для работы с файлами целиком. Т.е. если нужно частичное изменение файла в транзакции, то все равно придется делать полноценную копию. UPD 3: На просторах сети нашлось реализованное решение файловых транзакций - .NET Transactional File Manager. Проект доступен в виде NuGet-пакета, но последнее обновление было в сентябре 2013. Одно из двух: либо в проекте сделано все возможное, либо он просто заброшен. Второе вероятнее.

четверг, 12 декабря 2019 г.

Алгоритм поиска дубликатов файлов

#алгоритм #файловая_система


Меня интересуют алгоритмы поиска дубликатов файлов: среди множества каталогов нужно
найти и запомнить все файлы с одинаковым содержимым. В связи с большими объемами данных
для обработки требуется хороший алгоритм, советы и рекомендации насчет возможной оптимизации
его работы и т.п.

Я не прошу предоставить мне кусок готового кода или каким-либо иным способом полностью
реализованное задание, буду благодарен за любую полученную ценную информацию относительно
данного вопроса.
    


Ответы

Ответ 1



Вот решение в-лоб: Обходим все каталоги рекурсивно, сохраняем (path, filesize) в таблицу базы данных. Сортируем собранную таблицу по filesize, делаем выборку по всем записям где filesize неуникально. Для каждой пачки файлов с одинаковым filesize вычисляем хеш, проверяем на равенство. ??? PROFIT

Ответ 2



fdupes не смотрели? У него есть такой алгоритм и он вроде уже проверен временем

Ответ 3



Здесь какие-то люди уже 10 лет публикуют такое Windows приложение. Может Вам стоит связаться с ними.

Ответ 4



Hash для всего файла считать - плохая идея, если есть большие файлы Я сделал так: Для группы файлов с одинаковым размером делишь на группы по первого очередного байта в файле, для групп размером > 1 читаешь следующий байт и так по рекурсии Естественно, считывание байт должно быть буфферизованно, для каждого файла считываешь блоки в неск. килобайт, пототом оттуда выбираешь побайтно

вторник, 10 декабря 2019 г.

Подгрузка и чтение локального XML при помощи JS

#javascript #ajax #json #xml #файловая_система


В настоящий момент, из-за политики безопасности хромиума, нельзя подгружать локальные
файлы через ajax без аргумента «--allow-file-access-from-files». Но мне в текущий момент
требуется создать веб-приложение, где базой данных является xml-файл (в крайнем случае
json), находящийся рядом с index.html. Подразумевается, что пользователь может запустить
это приложение локально. Есть ли обходные пути для кроссбраузерного (ie11+) чтения
xml-(json-) файла, без оборачивания его в функцию и переименования в формат js?



UPD

Есть код для IE, но с ним, как оказалось, проблем никаких и не было

function loadXMLFile(filename) {
    return new Promise(function(resolve, reject) {

        // Если мы имеем дело с IE, то работает с ActiveX-контентом
        if('ActiveXObject' in window) {
            // Получение xml-текста из файла для осла
            var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
            xmlDoc.async = false;
            xmlDoc.load(filename);

            resolve(xmlDoc.xml);

        } else {

            // Если мы работаем с нормальными браузерами,
            // то здесь необходимо как-то получить xml-файл
            // ...
            // 
        }

    }
}

    


Ответы

Ответ 1



Возможны следующие варианты: Расширение браузера. Оно работает почти как локальные файлы (в том смысле, что файлы статические и лежат в файловой системе) - но для доступа к ресурсам используется не схема file:///path/to/file, а схема chrome-extension:///{guid}/path/to/file. В этой схеме нет никаких ограничений на доступ к ресурсам вашего же приложения. Все, что вам нужно - составить правильный манифест и упаковать приложение в архив. Ну, и добавить его в браузер, конечно же. Вот тут есть полный формат файла манифеста: https://developer.chrome.com/extensions/manifest Минимальный манифест - вот такой: { "manifest_version": 2, "name": "Мое приложение", "version": "1.0", "default_locale": "ru", "description": "Описание приложения", "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" } } Если я ничего не напутал - такого минимального манифеста хватит для того чтобы просто отобразить статические файлы. Для того чтобы добавить приложение в хроме - надо опубликовать его в магазине приложений, но для хромиума это не обязательно. Локальный сервер. В сети имеется достаточно легковесных веб-серверов, которые не требуют установки и которые могут отдавать статические файлы по какому-нибудь URL вида http://127.0.0.1:8081/. Или же, наоборот, можно сделать приложение, которое требует установки и во время этой самой установки поднимает сайт на IIS. Статический сайт поднять не так и сложно, сложнее поставить сам IIS автоматически (это точно возможно, но не помню как). Встроенный браузер. Если встроить хромиум в ваше приложение - можно "подсунуть" ему любые файлы по нестандартной схеме. Или по стандартной - но выставив все нужные флаги. Из готовых решений на слуху Electron. Помимо прочих возможностей, Electron позволяет хранимому локально "клиентскому" коду использовать весь API Node.js.

Можно ли как-то разделить файл, занимающий почти весь диск, на два файла?

#файлы #файловая_система


Например, диск 250G, на нем файл размером 200G, который надо разбить на два файла
по 100G. Файловая система - какая угодно - интересны любые варианты, как это можно сделать.
    


Ответы

Ответ 1



Если ваша файловая система поддерживает разреженные файлы то общий алгоритм таков: открываем новый файл, перемещаем файловый указатель на то место, где должны находится последние N мегабайт файла, переписываем эти N мегабайт. Закрываем файл источник, усекаем его на размер переписанной части (системный вызов truncate() / ftruncate()). Опять открываем файл, переписываем предпоследние N мегабайт (правильно выставив файловый указатель в файле назначения). Повторяем до тех пока не перепишем все. Рекомендуется писать большими частями, что бы не было сильной дефрагментации в ФС и частями, кратными размеру блока файловой системы (обычно 4кб). При таком подходе каждую часть файла мы перепишем только 1 раз и сразу на свое место, так как разреженные файлы занимают на диске столько места, сколько в них записано, не зависимо от смещений по которым писали, т.е. при записи более ранней части ФС добавляет на своем уровне блоки в начало цепочки занятых файлом. На операционных системах unix/linux эти операции можно проделать из командной строки с использованием утилит dd для записи порции файла по требуемому смещению и truncate для усечения файла источника до требуемой длины. Будьте осторожны и аккуратны, я предупредил :) Примерно так: dd if=файл1 of=файл2 bs=1M count=51200 seek=51200 skip=51200 truncate -s 161061273600 файл1 dd if=файл1 of=файл2 bs=1M count=51200 seek=0 skip=0

Ответ 2



Я правильно понимаю, что нужно делить файлы на несколько частей и затем через какой-то инструментарий получать к ним прозрачный доступ, как к обычным файлам? Т.е. в идеале пользователь даже не догадывается, что файл расположен на нескольких дисках, так? RAID-подобные системы, видимо, рассматривать не будем. У меня только пара идей для реализации подобного: Просто написать функцию, которая будет читать куски файлов, расположенных на разных дисках, последовательно. Данные о файлах хранятся в текстовом файле. Для разбиения тоже своя функция, которая как раз делит файл, записывает на разные диски и обновляет текстовый файл. Плюсы: самая простая реализация. Минусы: использование только в своих программах, сторонние пользовать не смогут. Перехват системных функций работы с файлами. Можно реагировать на определенное расширение, можно на имя файла. Перехватчику необходимо знать точные размеры файлов, хранящихся на разных дисках, и поставлять данные, в зависимости от того, с каким смещением от начала файла сейчас ведется чтение. Запись таких файлов делать спец. утилитой, которая делит файлы и пишет параметры разделения в некий файл данных, регулярно читаемый перехватчиком. Плюсы: реализовать не сильно сложно, сторонние программы также могут читать такие файлы прозрачно. Своя файловая система. Переписать тот же FAT32, позволив таблице размещения файлов ссылаться не только на кластеры текущего диска, но и на кластеры других дисков (или даже не кластеры, а просто на файлы других дисков). Идея хороша тем, что позволяет не менять FS других дисков, главное, чтобы начало файла находилось именно на этом диске. Плюсы: сторонние программы также могут читать такие файлы прозрачно. Минусы: сложновато в реализации, придётся писать свой драйвер (не знаю, есть ли в сети исходники драйвера FAT32).

Ответ 3



В теории можно файл делить блоками. Например: 250(свободное место) - 200(размер файла) = 50 гигов на операции. Дальше берем 40(поменьше взял) из первого и пишем во второй. После чего переписываем первый. Получаем два файла 160 и 40. Дальше еще две итерации и файлы разделены: 120 и 80 100 и 100

понедельник, 9 декабря 2019 г.

Как ускорить чтение директорий файловой системы?

#c_sharp #net #winapi #файловая_система


Надо сканировать огромное количество файлов и папок. Обычно для этого используется
метод Directory.EnumerateFileSystemEntries. 
Возможно ли как-то ускорить процесс чтения файловой системы?
    


Ответы

Ответ 1



Можно ускорить почти в два раза, если использовать WinAPI. // Microsoft (R) Roslyn C# Compiler version 1.1.0.51204 using System.Runtime.InteropServices; [DllImport("kernel32.dll")] static extern int GetLastError(); [DllImport("kernel32.dll")] static extern bool FindClose(IntPtr handle); class SafeHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { private SafeHandle() : base(true) { } protected override bool ReleaseHandle() { return FindClose(this.handle); } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct DATA { // WIN32_FIND_DATA public FileAttributes FileAttributes; public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime; public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime; public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime; public uint FileSizeHigh; public uint FileSizeLow; public uint Reserved0; public uint Reserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string FileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string AlternateFileName; } [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern SafeHandle FindFirstFileEx(string name, int i, out DATA data, int so, IntPtr sf, int f); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern bool FindNextFile(SafeHandle h, out DATA data); IEnumerable ReadPath(string path) { // например: "c:\" | "c:\*.png" | "c:\*" DATA d; var p = String.Concat(@"\\?\", path.TrimEnd('\\', ' ')); using (var h = FindFirstFileEx(p, 1, out d, 0, IntPtr.Zero, 2)) { if (h.IsInvalid) throw new System.ComponentModel.Win32Exception(GetLastError()); yield return d; while (FindNextFile(h, out d)) if (d.FileName != "..") yield return d; var e = GetLastError(); // e = 18 -- дошли до конца if (e != 18) throw new System.ComponentModel.Win32Exception(e); } } Получить файлы и папки в c:\: foreach (var d in ReadPath(@"c:\*")) Console.WriteLine("name=" + d.FileName); Получить файлы в c:\temp\ и подпапках: class File { public string Path; public string Name; } IEnumerable Scan(string path) { foreach (var d in ReadPath(path + "*").Skip(1)) { // сканируем только подпапки, а junction пропускаем if ((d.FileAttributes & FileAttributes.Directory) != 0 && (d.FileAttributes & FileAttributes.ReparsePoint) == 0) foreach (var f in Scan(path + d.FileName + "\\")) yield return f; else yield return new File { Path = path, Name = d.FileName }; } } long ToLong(uint high, uint low) { return (long)(((UInt64)high << 0x20) | (UInt64)low); } foreach (var f in Scan(@"c:\temp\")) Console.WriteLine( f.Path + "\t\t\t" + f.Name + " size=" + ToLong(d.FileSizeHigh, d.FileSizeLow));

Ответ 2



Нужно индексировать FS в базу данных и работать потом с базой.

воскресенье, 8 декабря 2019 г.

Найти суммарный размер всех регулярных файлов в каталоге, рекурсивно обходя все подкаталоги

#linux #файловая_система


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

У меня есть написанная программа, которая успешно выдает какие-то числа, но я не
знаю, как убедиться в том, что она выдает правильный ответ. Подскажите, пожалуйста:

1) Можно ли как-то решить исходную задачу средствами bash (команда ls и так далее)? 

2) Какие могут быть "крайние" случаи, на которых программу стоит протестировать?
Ну кроме пустого каталога.
    


Ответы

Ответ 1



Чтобы подсчитать суммарный размер обычных файлов (S_ISREG) в заданном дереве директорий, пропуская все symlinks, на Питоне: #!/usr/bin/env python3 import os from contextlib import suppress def get_tree_size_scandir(path): """Return total size of all regular files in directory tree at *path*.""" size = 0 for entry in os.scandir(path): with suppress(OSError): # ignore errors for entry & its children if entry.is_dir(follow_symlinks=False): # directory size += get_tree_size_scandir(entry) elif entry.is_file(follow_symlinks=False): # regular file size += entry.stat(follow_symlinks=False).st_size return size if __name__ == "__main__": import sys print(get_tree_size_scandir(sys.argv[1])) Пример: $ ./get-tree-size /usr 7217750930 Вывод показывает, что общий размер всех обычных файлов в /usr директории, около 7 GB. Python, listdir() Для проверки, я реализовал get_tree_size(), не используя os.scandir(): #!/usr/bin/env python3 import os import stat from contextlib import suppress _dir_flags = os.O_RDONLY def get_tree_size_listdir_fd(fd): """Return total size of all regular files in directory tree at *fd*.""" size = 0 for name in os.listdir(fd): with suppress(OSError): # ignore errors for entry & its children st = os.lstat(name, dir_fd=fd) # don't follow symlinks if stat.S_ISDIR(st.st_mode): # directory top_fd = os.open(name, _dir_flags, dir_fd=fd) try: size += get_tree_size_listdir_fd(top_fd) finally: os.close(top_fd) elif stat.S_ISREG(st.st_mode): # regular file size += st.st_size return size if __name__ == "__main__": import sys print(get_tree_size_listdir_fd(os.open(sys.argv[1], _dir_flags))) Результаты одинаковые в данном случае, но в общем случае они могут отличаться (например, os.listdir() возвращает список (сразу все имена), а os.scandir() возвращает итератор, поэтому os.scandir() может учесть больше имен, а os.listdir() пропустит всю директорию, если произойдёт ошибка c получением хотя бы одного имени в директории). Код для примеров адаптирован из Python issue: PEP 471 implementation: os.scandir() directory scanning function. Внимание: размер файла и занимаемое место на диске могут отличаться. Bash, du 1) Можно ли как-то решить исходную задачу средствами bash (команда ls и так далее)? Можно, конечно, но результаты могут немного отличаться (см. тестовые случаи). Если нужны точные результаты, то несложно написать программу, с точным необходимым поведением как показывают примеры кода на Питоне выше. du -bs . возвращает значение, которое превышает суммарные размеры файлов, например: $ ls -l total 8 -rw-rw-r-- 1 me me 820 Oct 25 22:59 get_tree_size_fd.py -rw-rw-r-- 1 me me 631 Oct 25 23:07 get_tree_size_scandir.py Суммарный размер: 820 + 631 == 1451: $ python3 get_tree_size_fd.py . 1451 что ожидаемо (Питон возвращает правильный результат), но du возвращает неверный результат: $ du -bs . 5547 . -b опция уже включает в себя --apparent-size (то есть результат уже не отражает занимаемое место на диске -- как и хотелось). @avp упомянул: du считает также размеры всех каталогов, которые они занимают на диске. Что подтверждается экспериментами: $ mkdir dir # создаём пустую директорию $ python3 get_tree_size_fd.py . # результат ожидаемо не изменился 1451 $ du -bs . 9643 . Результат для du стал больше, что согласуется c комментарием @avp. Если выключить --apparent-size, то du возвращает занимаемое место на диске: $ du -s -B1 . 16384 . Что ещё больше отличается от суммарного размера файлов. 2) Какие могут быть "крайние" случаи, на которых программу стоит протестировать? Ну кроме пустого каталога. Потестировать имена файлов, директорий, начинающихся на точку (.zshrc, .ssh). Потестировать на директории со специальными файлами, например, /dev директория может содержать /dev/sda файл, который не является обычным файлом (это диск -- блочное устройство S_ISBLK) или FIFO (S_ISFIFO) (можно создать командой: mkfifo /tmp/named_pipe). Или потестировать на директориях с нечитаемыми записями, например, из-за недостатка прав доступа (командой chmod можно подготовить). И, конечно, потестировать на директориях, содержащих символические ссылки (S_ISLNK), которые ссылаются как на обычные файлы так и на другие директории. Для проверки надёжности, можно сгенерировать глубоко-вложенные директории с именами записей разной длины, состоящих из произвольных байтов (всё кроме слэша / и нулевого байта '\0', если локальная система не вносит своих ограничений). С, nftw() Для сравнения, можно посмотреть на примеры кода на С/С++. Для рекурсивного обхода дерева директорий, можно nftw() использовать: #define _XOPEN_SOURCE 500 #include #include #include #include int main(int argc, char* argv[]) { if (argc > 2) { fputs("Usage: get-tree-size []\n", stderr); exit(2); } int flags = FTW_PHYS; // do not follow symlinks int nopenfd = 100; // maximum number of directories that nftw() may hold // open simultaneously uintmax_t size = 0; int visit_path(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) // nested function -- gcc extension { if (typeflag == FTW_F) // regular file size += sb->st_size; return 0; // continue }; const char *dirpath = (argc == 2) ? argv[1] : "."; // default is the current working directory if (nftw(dirpath, visit_path, nopenfd, flags)) exit(1); return printf("%" PRIuMAX "\n", size) < 0; } Пример: $ gcc get-tree-size-ftw.c && ./a.out Чтобы передать дополнительные переменные (size) в visit_path() обратный вызов, gcc позволяет использовать вложенные функции. В общем случае, для более тонкого контроля обхода дерева директорий, к примеру, чтобы пропустить всё внутри .git и других подобных директорий, есть fts_open() API, добавив fnmatch() API, можно реализовать аналог команды: $ find -name .git -prune -o -type f -name \*.txt -print С, readdir() При желании, можно руками с помощью readdir() рекурсивный обход директории выполнить: #define _XOPEN_SOURCE 700 #include #include #include #include #include #include static uintmax_t get_tree_size_readdir(int dirfd) { DIR *dirp; if (!(dirp = fdopendir(dirfd))) return 0; //NOTE: ignore errors from open,openat,fdopendir here uintmax_t size = 0; for (struct dirent* entry; (entry = readdir(dirp)); ) { // skip ".", ".." entries size_t namelen = strlen(entry->d_name); if (entry->d_name[0] == '.' // 1 <= namelen <= NAME_MAX && (namelen == 1 || (entry->d_name[1] == '.' && namelen == 2))) continue; struct stat statbuf; if(fstatat(dirfd, entry->d_name, &statbuf, AT_SYMLINK_NOFOLLOW)) continue; //NOTE: ignore errors if (S_ISREG(statbuf.st_mode)) // count size of regular files only size += statbuf.st_size; else if (S_ISDIR(statbuf.st_mode)) { // count the size in subdirectories int fd = openat(dirfd, entry->d_name, O_RDONLY); size += get_tree_size_readdir(fd); //NOTE: if nested deeply; it may exceed `ulimit -n` close(fd); } } //NOTE: ignore readdir() errors closedir(dirp); //NOTE: let the caller to invoke rewinddir() if necessary return size; } директория задаётся с помощью dirfd это позволяет избежать каждый раз от корня все пути просматривать, так как entry->d_name содержит только последнюю часть пути. В противном случае пришлось бы создавать путь от входной (с которой вызов начался) директории каждый раз, прежде чем путь в stat() передать специальные имена "." и ".." явно пропускаются используется AT_SYMLINK_NOFOLLOW, чтобы не следовать по символическим ссылкам, чтобы получить информацию о самой записи (entry) ошибки по индивидуальным записям явно игнорируются #include #include int main(int argc, char* argv[]) { if (argc > 2) { fputs("Usage: get-tree-size []\n", stderr); exit(2); } const char *dirpath = (argc == 2) ? argv[1] : "."; // default is the current working directory return printf("%" PRIuMAX "\n", get_tree_size_readdir(open(dirpath, O_RDONLY))) < 0; } Пример: $ gcc get-tree-size-readdir.c && ./a.out С++, В C++ рекурсивно обойти дерево директорий можно используя библиотеку: #include #include #include namespace fs = std::experimental::filesystem; int main(int argc, char* argv[]) { if (argc > 2) { std::cerr << "Usage: get-tree-size []\n"; std::exit(2); } uintmax_t size = 0; fs::path dirpath = (argc == 2) ? argv[1] : fs::current_path(); for (auto&& entry : fs::recursive_directory_iterator( dirpath, fs::directory_options::skip_permission_denied)) { std::error_code ignore_error; if (fs::is_regular_file(fs::symlink_status(entry, ignore_error))) { size += fs::file_size(entry); } } std::cout << size << '\n'; } символические ссылки, указывающие как на директории так и на обычные файлы пропускаются "." и ".." записи также пропускаются ошибки доступа и ошибки при чтении статуса файла игнорируются, но цикл может исключения выбрасывать в случае других ошибок. Пример: $ g++ -std=c++11 *.cc -lstdc++fs && ./a.out (для ). В С++17 можно просто #include использовать. Библиотека также доступна как #include : $ sudo apt-get install libboost-{file,}system-dev # install Boost on Ubuntu $ g++ -std=c++11 *.cc -lboost_{file,}system && ./a.out Все варианты кода для подсчёта суммарного размера выдают один и тот же результат в обычных случаях, но возможны отличия, когда исключительные ситуации по разному обрабатываются. Производительность ограничивается скоростью диска. Если мета-данные уже закэшированы в памяти, то вариант с медленнее, чем nftw() и readdir(), которые похоже себя ведут. код только немного медленнее кода на Питоне.

Ответ 2



du -s /your/path/* Где /your/path/ - путь к папке. Размер указывается в килобайтах. Если нужно в байтах, то: du -sb /your/path/*

воскресенье, 1 декабря 2019 г.

Получить доступ к папке

#c_sharp #net #файловая_система


Нужно получить права на чтение и запись залоченого фолдера.

Или как сделать следующий код валидным:

        DirectoryInfo dir = new DirectoryInfo(@"D:/System Volume Information");

        foreach (var item in dir.GetDirectories())
        {
            Console.WriteLine(item.FullName);
        }


Желательно что бы работало с System Volume Information

Следующий код не работает тоже:

    [Flags]
    enum MoveFileFlags
    {
        MOVEFILE_REPLACE_EXISTING = 0x00000001,
        MOVEFILE_COPY_ALLOWED = 0x00000002,
        MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
        MOVEFILE_WRITE_THROUGH = 0x00000008,
        MOVEFILE_CREATE_HARDLINK = 0x00000010,
        MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName,
MoveFileFlags dwFlags);

    static void Main(string[] args)
    {
        string path = @"D:/System Volume Information";

        DirectoryInfo dir = new DirectoryInfo(path);

        MoveFileEx(path, path + "2", MoveFileFlags.MOVEFILE_REPLACE_EXISTING);

        string msg = new Win32Exception(Marshal.GetLastWin32Error()).Message;

        Console.WriteLine(msg);
    }

    


Ответы

Ответ 1



Ага, я нашёл, как Far это делает. Он не просто запускает процесс от имени администратора (на самом деле, дочерний процесс, с которым связывается через pipe). Этот самый дочерний процесс, будучи запущенным от имени администратора, имеет право затребовать себе привилегии Backup и Restore (вот официальный метод, как сделать такое на чистом WinAPI). Что он и делает. Имея эти привилегии, дочерний процесс может делать всё. Мораль: вы можете ограничить в правах административный процесс, но он может снять ваши ограничения. Для читателей, вот работающий код: (разумеется, вы должны добавить манифест, как указано в ответе @Dmitry) class Program { static public void Main() { using (var outf = File.CreateText(@"D:\dirlist.txt")) { if (!RequestSeBackupPrivilege()) { outf.WriteLine("Cannot request privilege: "); return; } try { string path = @"D:\System Volume Information"; var dir = new DirectoryInfo(path); foreach (var item in dir.GetFileSystemInfos()) outf.WriteLine(item.FullName); } catch (Exception ex) { outf.WriteLine("Exception:"); outf.Write(ex); } } } static bool RequestSeBackupPrivilege() { LUID luid; if (!LookupPrivilegeValue(null, "SeBackupPrivilege", out luid)) return false; TOKEN_PRIVILEGES_SINGLE tp = new TOKEN_PRIVILEGES_SINGLE { PrivilegeCount = 1, Luid = luid, Attributes = SE_PRIVILEGE_ENABLED }; IntPtr hToken; return OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken) && AdjustTokenPrivileges( hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero) && (Marshal.GetLastWin32Error() != ERROR_NOT_ALL_ASSIGNED); } const int SE_PRIVILEGE_ENABLED = 0x00000002; const int TOKEN_QUERY = 0x00000008; const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; const int ERROR_NOT_ALL_ASSIGNED = 1300; [DllImport("kernel32.dll", ExactSpelling = true)] static extern IntPtr GetCurrentProcess(); [StructLayout(LayoutKind.Sequential)] struct TOKEN_PRIVILEGES_SINGLE { public UInt32 PrivilegeCount; public LUID Luid; public UInt32 Attributes; } [StructLayout(LayoutKind.Sequential)] struct LUID { public uint LowPart; public int HighPart; } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool LookupPrivilegeValue( string lpSystemName, string lpName, out LUID lpLuid); [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool OpenProcessToken( IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] static extern bool AdjustTokenPrivileges( IntPtr htok, bool disall, ref TOKEN_PRIVILEGES_SINGLE newst, int len, IntPtr prev, IntPtr relen); } Определения структур данных взяты на pinvoke.net.

Ответ 2



Насколько я помню, этот фолдер не залочен, а требует повышенных привилегий администратора. Чтобы повысить привилегии вашего приложения, добавьте в проект файл манифеста (выполнив Project -> Add New Item -> "Application Manifest File"), а в нем откорректируйте значение узла requestedExecutionLevel следующим образом:

Ответ 3



VladD прав. Это защищенная системная папка, доступ к которой осуществляется при помощи прав Local System. Вам нужно запустить свое приложение от Local System, сделать это можно следующими способами: Использовать планировщик задач (TaskScheduler). Написать службу и запустить приложение из нее. Внедриться в какой-нибудь LocalSystem-процесс и оттуда запустить свою программу. Использовать psexec (Sysinternals) с ключом -s.