Задача:


Есть ПК без интернета но есть возможность перекинуть файл по USB. Есть планшет с интернетом с которого этот файл можно перекинуть. На планшет можно скачать нужный торрент но не достаточно свободного места. Файл в торренте один и большой.


Путь к решению:


Я запустил торрент на загрузку. Когда свободное место почти подошло к концу я поставил загрузку на паузу. Подключил планшет к ПК и переместил файл с планшета на ПК. Отжал паузу и к моему удивлению файл был снова создан и торрент продолжил качаться дальше как ни в чем не бывало.


Благодаря тому что торрент клиент устанавливает sparse флаг файлу в который записывает полученные данные система не пытается зарезервировать сразу 16GB и не возникнет ошибки при попытке записи в файл дальше 4GB.


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


Мне казалось что такая простая программка должна быть в интернете. Неужели никто не сталкивался с такой задачей? Но я понял что даже не знаю по каким ключевым словам её искать. Поэтому я быстро накидал Lua скрипт под эту задачу а теперь уже и оптимизировал его. Им и хочу поделиться.


Загружаем торрент по частям


  1. запускаем загрузку торрента на первом устройстве
  2. ждём пока заполнится ПЗУ
  3. ставим загрузку на паузу
  4. переносим файл на второе устройство и добавляем к имени файла цифру
  5. возвращаемся к первому пункту до тех пор пока файл не скачается полностью

Сливаем части в один файл


После того как получена последняя часть необходимо собрать их в один целый файл.


Задача простая:


  1. Читаем все части одновременно
  2. Если в какой то части в позиции не нулевой байт то пишем на выход его иначе пишем ноль

Функция merge_part принимает массив потоков streams_in из которых читает часть размером buffer_length и возвращает результат слияния частей из разных потоков.


function merge_part(streams_in, buffer_length)
    local out_part
    for _, stream in ipairs(streams_in) do
        local in_part = stream:read(buffer_length)

        if not out_part then
            out_part = in_part -- просто копируем часть из первого файла
        elseif in_part and #in_part > 0 then

            if #out_part < #in_part then
                out_part, in_part = in_part, out_part
            end

            if out_part ~= in_part  -- данные различаются
                and in_part:find("[^\0]")   -- есть данные в in_part
                and out_part:find("\0", 1, true) -- есть пустые места в out_part
            then 
                local find_index = 1
--[[

Функция string.gsub подходит для задачи так как найдёт кусочки заполненные нулями и поставит то что передано ей.


--]]
                out_part = out_part:gsub("\0+", function(zero_string)

                    if #in_part < find_index then
                        return -- не на что менять
                    end
--[[

string.gsub не передаёт позицию в которой был найдено совпадение. Поэтому делаем параллельный поиск позиции zero_string при помощи функции string.find. Достаточно найти первый нулевой байт.


--]]
                    local start_index = out_part:find("\0", find_index, true)
                    find_index = start_index + #zero_string

--[[

Теперь если в in_part есть данные для out_part копируем их.


--]]
                    if #in_part >= start_index then
                        local end_index = start_index + #zero_string - 1
--[[

Вырезаем из in_part часть соответствующую последовательности нулей.


--]]
                        local part = in_part:sub(start_index, end_index)

                        if (part:byte(1) ~= 0) or part:find("[^\0]") then
--[[

В part есть данные.


--]]
                            if #part == #zero_string then
                                return part
                            else
--[[

part оказался меньше чем последовательность нулей. Дополняем его ими.


--]]
                                return part..zero_string:sub(1, end_index - #in_part)
                            end
                        end
                    end
                end)
            end
        end
    end
    return out_part
end

Заключение


Таким образом удалось скачать и собрать этот файл на ПК. После слияния я вытащил с планшета торрент файл. Установил на ПК торрент клиент и проверил им файл.


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


Использовались:


  1. Торрент клиент Flud на планшете.
  2. Торрент клиент qBittorent на ПК.
  3. Lua скрипт

Комментарии (44)


  1. HansHelmut
    29.10.2019 17:51
    +2

    Не пробовали использовать планшет как usb модем?


    1. ivan386 Автор
      29.10.2019 17:57

      Ну в данном случае этот ПК нельзя было выпускать в интернет. Но у других может быть вариант что провайдер ограничивает раздачу интернета с планшета или телефона.


      1. iig
        29.10.2019 18:02
        +2

        В интернет нельзя, но подключать USB можно, и файлы из интернетов можно :)
        Ох уж эта безопасность ;)


      1. vmspike
        29.10.2019 20:14
        +1

        Вариант, когда провайдер ограничивает раздачу, обычно можно обойти так:


        • поставить на мобильное устройство socks5 сервер (есть разные приложения)
        • активировать точку доступа на мобильном устройстве
        • с ПК подключиться к точке доступа используя ручные настройки (вместо DHCP) без задания шлюза
        • запустить на ПК торрент клиент и прописать ему настройки socks прокси.

        Таким образом у ПК будет доступ в интернет только у программ, настроенных работать через прокси.


        1. bodqhrohro
          30.10.2019 17:47

          и прописать ему настройки socks прокси
          Проще ProxyCap или proxychains поставить, они умеют трафик любой программы через прокси заворачивать, даже если сама программа настройку прокси не поддерживает. Причём с поддержкой удалённого DNS.


          1. vmspike
            30.10.2019 22:52

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

            Для обхода провайдерских ограничений на раздачу трафика есть и другие способы, которые могут быть более удобными, но вариант с socks один из самых простых в настройке, не нужно никаких плясок с VPN и файерволом, не нужен рут на мобильном, работает довольно надёжно с любыми ОС.
            Из возможных уязвимых мест такого подхода — нешифрованный трафик с ПК, там может быть не мобильный user-agent и другая информация, если провайдер заморочится, могут возникнуть подозрения.


          1. mayorovp
            31.10.2019 09:06

            Любой ли? Насколько я знаю, у таких решений всегда проблемы с протоколами, где информация о конечной точке передаётся по сети в сообщениях прикладного уровня. И торренты к этой категории как раз относятся.


            1. bodqhrohro
              31.10.2019 21:10

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


      1. iBurokrat
        30.10.2019 15:01

        При этом оператор не ограничивает торренты?


  1. Squoworode
    29.10.2019 18:40

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


    1. ivan386 Автор
      29.10.2019 18:44

      Если поставить на паузу переместить файл и закрыть клиент, то после открытия и запуска так и происходит.


      1. Angmarets
        29.10.2019 19:56
        +1

        я думаю имееться в виду повезло с клиентом. С uTorrent это не прокатит например. Файл вы не удалите пока не ОСТАНОВИТЕ закачку. Не пауза, а именно стоп. Затем при попытке продолжить закачку она упадет с ошибкой «файл не найден» пока не запустите «обновить хэш»


        1. ivan386 Автор
          29.10.2019 20:56

          Вот только сейчас узнал что есть uTorrent под Android.


        1. khim
          30.10.2019 00:51

          Файл вы не удалите пока не ОСТАНОВИТЕ закачку.
          Это кто ж вам запретит его удалить?

          Другое дело, что удалить-то вы его удалите, а вот место не освободится… можно попробовать fallocate, конечно, но чем это закончится не очень ясно: там были проблемы с безопасностью и на многих телефонах fallocate выпилили… даже CTS тест есть.


        1. zzzzzzzzzzzz
          31.10.2019 00:34

          Torrent-клиент очень странный. Мало того, что позволяет удалить находящийся «в работе» файл, так ещё и выставление флажка sparse — это, по сути, ошибка в клиенте.


          1. ivan386 Автор
            31.10.2019 08:48
            +1

            Почему установка sparse флага это ошибка?


            1. zzzzzzzzzzzz
              31.10.2019 10:21

              Этот флажок программа должна выставлять на файле, если она предполагает, что там кучи нулей будут в содержимом. Для торрента это (в общем случае) неверно. Функциональности типа «скачать только вторую половину файла» я в торрент-клиентах тоже не видел. Поэтому торрент-клиенту, вроде бы, незачем помечать файл sparse. Единственная возможная причина — «не выделять сразу весь объём файла, а то система из соображений безопасности начнёт его весь заполнять нулями, а это долго». Но это сомнительная оптимизация, поскольку в результате файл получится сильно фрагментированным.


              1. ivan386 Автор
                31.10.2019 10:46
                +1

                Ещё вариант что пользователь может отменить скачивание нескольких файлов из торрента а начало и конец файла могут находиться в одном блоке с соседними файлами. Их всеравно прийдётся скачать а без sparse флага файл будет занимать место полностью по большей части являясь пустым.


                Так-же если отменили скачивание когда уже в файле есть несколько частей то они останутся и будут раздаваться а файл опять же не будет занимать много места.


                Ну и удаление наверно доступно по тем же причинам. Так как в уже не нужном файле может быть много загруженных данных. Его можно удалить и освободить место для остальных.


                Баг здесь только в том что клиент возможно не проверяет то что отдаёт. Хотябы то что блок заполнен нулями а должны быть данные.


                Ну и клиент будет вечно бороться с антивирусом если втророй будет удалять файл а первый его перекачивать автоматически.


                1. zzzzzzzzzzzz
                  31.10.2019 22:17

                  Куски начал и концов ненужных файлов uTorrent (например) помещает в отдельный файл со странным именем ("~uTorrentPartFile_207807000.dat"). И это достаточно удобный вариант, т.к. недозагруженные куски не перемешиваются с полными файлами при просмотре каталога пользователем.

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

                  А возможность удаления файла — это всё-таки очень странно. Логично было бы в начале скачивания открыть файл на запись и закрыть только после окончания загрузки (и тогда он так просто не удалится). А они, получается, на каждый кусочек: открыли файл — записали кусочек — закрыли файл?


                  1. khim
                    31.10.2019 23:49

                    А вот фрагментация получающихся таким образом файлов должна быть просто ужасающая (интересно было бы проверить численно).
                    На SSD пофиг…

                    Логично было бы в начале скачивания открыть файл на запись и закрыть только после окончания загрузки (и тогда он так просто не удалится).
                    Ещё раз: не лезьте со своими Windows-подходами в телефон. Удалится прекрасно.

                    А они, получается, на каждый кусочек: открыли файл — записали кусочек — закрыли файл?
                    Разумный подход, на самом деле: файлов в торренте может быть и сто тысяч, а файловые дескрипторы — ограниченный ресурс.


  1. Demosfen
    29.10.2019 21:43

    Планшет рутануть, замонтировать с компа директорию через самбу и хоть терабайты можно выносить. Или через otg (если планшет умеет) внешний диск зацепить. Но блин так из кусочков каждый раз собирать.....


    1. ivan386 Автор
      29.10.2019 21:53

      Внешний жёсткий диск по otg не получилось потключить так как диску нужно было внешнее питание. Без него планшет диск не тянет.


      1. saege5b
        29.10.2019 22:01
        +1

        Можно сколхозить шнур отг+внешнее питание, на 4пда даже тема есть по этому вопросу.


        1. Demosfen
          29.10.2019 22:03
          +1

          Или активный хаб...


    1. EndUser
      30.10.2019 00:07

      Подскажите, пожалуйста, как? DroidSMB, SambaDroid и IceColdApps как-то не работают, LANdrive приходится каждый час, каждый раз руками подталкивать.


      1. Demosfen
        30.10.2019 05:47

        Честно говоря, уже не вспомню каким именно клиентом это делал на стике мк802-iii, но работало стабильно. Помню только что гуглил что-то типа android mount smb и дальше перебирал все известные клиенты на 4пда.


  1. nochkin
    29.10.2019 22:33
    +2

    Если я правильно понял, то после паузы клиент будет отдавать нули другим peer'ам, думая что эти блоки он уже скачал. Или я что-то напутал?


    1. ivan386 Автор
      29.10.2019 22:45

      Скорей всего так и есть. Поэтому после полного скачивания нужно обязательно запустить перепроверку торрента чтоб клиент понял какие блоки у него действительно есть и раздавал только их. Ну и надо снять галочку с файла чтоб он не пытался его опять докачать.


      1. nochkin
        29.10.2019 23:29

        Как запустить перепроверку после полного скачивания если полное скачивание невозможно из-за нехватки места на планшете?


        1. ivan386 Автор
          29.10.2019 23:35

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


          1. nochkin
            30.10.2019 05:20

            Так торрент клиент на планшете не сможет проверить, так как куски файла не доступны полностью.
            Самый простой пример — торрентом является один какой-нибудь ISO файл.


            1. ivan386 Автор
              30.10.2019 07:16

              В последнем цикле после проверки останутся части которые полнстью скачаны за последний цикл а также части которые которые были скачаны частично на начало цикла. Части которые были скачаны частично на момент запуска последнего цикла скачаются 2 раза так как хеш в первый раз не сойдётся.


              Можете провести эсперимент. У меня всё получилось с клиентом Flud.


            1. mayorovp
              30.10.2019 10:15

              А что помешает ему проверить? Нули на месте тех кусков файла вполне доступны клиенту.


  1. gromazeka
    30.10.2019 16:25
    -1

    Где то в азии начинаются проекты по 6g со скоростью до 1 Тбит/с, миниатюрные флеш накопители обьемом 256-512 Гб уже давно стали ширпотребом, а он тут файлы по 4Гб кусками перекидывает, мог бы еще усложнить fat системой и кидать по 2Гб (но это наверное в след. статье ?), куда же ты паря смотришь, что ты делаешь, иди учись, работай и не занимайся ерундой!


  1. Bersano
    30.10.2019 17:05

    есть торрент-клиенты с последовательной скачкой данных (например, qbittorent), то есть не пришлось бы потом искать части и сравнивать их с null'ами. Не знаю правда, есть ли такая версия торрент-клиента для планшета.


    1. ivan386 Автор
      30.10.2019 17:19

      Во Flud тоже есть последовательная скачка. Это может ускорить слияние. Но даже при ней не представляю как алгоритм улучшить. На данный момент он упирается только в скорость чтения/записи.


      Ну и если каждый после такой процедуры оставит хвост файла на раздаче то может оказаться что доступен останется только он. А при рандомной загрузке у каждого в последней части будут разные места заполнены.


  1. legolegs
    30.10.2019 18:21
    +1

    Я как всегда не смог сдержать зуд реализовать всё то-же самое шеллскриптом.
    Если исходные версии файла из-под торрента числом 4 и называются payload_1, payload_2, payload_3 и payload_4 и известно, что торрент побит на сегменты в 1Мб (можно посмотреть в клиенте или выставить своё значение, хоть 4кб), то это скрипт склеит файл:


    mkdir brokenfragments
    cd brokenfragments
    for i in {1..4}; do
        split --numeric-suffixes --bytes=1M --suffix-length=4 ../payload_${i} payload_${i}_
    done
    for i in payload_1_* ; do
        N=${i#payload_1_}
        for j in {1..4}; do
            if <payload_${j}_${N} tr -d '\0' | read -n 1 ; then
                cp payload_${j}_${N} payload_x_$N 
                continue;
            fi
        done;
    done
    cat payload_x_* > ../payload_x

    Если в исходном файле свои собственные нули, то возможны нюансы. Команда <файл tr -d '\0' | read -n 1 (со стековерфло) удаляет из файла "файл" нули и смотрит, не осталось ли чего; возвращает успех если не всё — нули.


    1. ivan386 Автор
      30.10.2019 19:22

      Я не спец по шеллскрипту но не думаю что операционная система обрадуется файлу на каждый 1MB данных при обьёме в 16GB каждой части. Пустые сегменты будут занимать 4KB но также будут иметь запись в файловой таблице.


      1. legolegs
        30.10.2019 19:28
        +1

        Винда не радуется, линуксу норм.


        Линейное чтение правильнее, но на шелле я пока не придумал как это сделать элегантно или хотя бы читаемо.


        1. mayorovp
          31.10.2019 09:11

          А на винде-то что не так? Или вы под "виндой" понимаете Проводник?


          1. legolegs
            31.10.2019 16:31

            Очень медленно работает. Тестовый прогон с 16кб фрагментами у меня весь день собирается, ещё не готово.


            1. iig
              31.10.2019 17:21

              Если вы пишете миллион файлов на FAT — так и будет.


              1. legolegs
                31.10.2019 18:51

                NTFS же. И только четверть миллиона.


                1. iig
                  31.10.2019 19:27

                  Возможно, это поможет.

                  Microsoft recommends turning off short filename creation if you have more than 300k files in a folder