Прежде, чем меня объявят съехавшим с катушек фриком, оговорюсь, что это делалось для борды, где наличествует всего 64MiBs оперативной памяти (из которой больше половины сжирал запущенный софт). И (из-за специфичности задачи), я решил юзать bash подцепленный к lighttpd через cgi (гусары — молчать!) Тащить туда тяжелую артиллерию (php или python), усложняло код серверсайд части, да и вообще пахло садомазохизмом.
Ладно, в общем потихонечку я дошел до ручки необходимости загружать на борду файлы через POST. Разумеется, начал я с гуглежа решения, и даже нашел одно. Но к сожалению, у него был недостаток.
Если посмотреть на тот код по ссылке, то можно увидеть примерно следующее
if [ "$REQUEST_METHOD" = "POST" ]; then TMPOUT=/tmp/fwupdate cat >$TMPOUT # Get the line count LINES=$(wc -l $TMPOUT | cut -d ' ' -f 1) # Remove the first four lines tail -$((LINES - 4)) $TMPOUT >$TMPOUT.1 # Remove the last line head -$((LINES - 5)) $TMPOUT.1 >$TMPOUT # Copy everything but the new last line to a temporary file head -$((LINES - 6)) $TMPOUT >$TMPOUT.1 # Copy the new last line but remove trailing \r\n tail -1 $TMPOUT | perl -p -i -e 's/\r\n$//' >>$TMPOUT.1 fi |
Невооруженным глазом видно, что все это добро складывается в несколько временных файлов. И вот тут мы имеем проблемы, так как у меня и флешка на этой борде из разряда слоупоков, и лишний раз нагружать ее ерейзами не хочется, и оперативной памяти не хватит, чтобы все это сделать в tmpfs. Мысли? Пришлось написать свою… реализацию.
#!/bin/bash OIFS="$IFS" read boundary read disposition read ctype read junk #Holy fuck, this sucks! #Due to \n\r line breaks we have 2 extra bytes per line read, #6 + 2 newlines == 10 junk bytes a=${#boundary} b=${#disposition} c=${#ctype} a=$((a*2+b+c+d+10)) IFS="${IFS}&:" set $QUERY_STRING dir=$1 file=$2 SIZE=$((HTTP_CONTENT_LENGTH-a)) echo "Content-Type: text/html" echo "" echo "Upload complete, $SIZE bytes stored<br>" echo "Written to $dir, filename $2 ($dir/$file)<br>" dd ibs=1 obs=512 count=$SIZE of=$dir/$file |
Вот и все! Ни временных файлов, ни нет необходимости буферизовать весь файл в памяти, как делает PHP. просто один небольшой dd и немного особой магии, для того, чтобы рассчитать откуда и сколько ддшить.
Разумеется, реализация тупа как дуб, ни каких POST переменных формочки, только сам файл. Потому имя и путь для файла я передаю GET’ом, разделенные ‘:’.
В таком виде, это, конечно, дыра в безопасности размером… Не будем говорить каких размеров. Но так как из-за спейифики задачи, мне вообще надо лазить по физической памяти камня из JS в браузере… Почему бы и нет. Только не забудьте пристрелить того, кто это решит использовать на боевом сервере в продакшне.
a=${#boundary}
b=${#disposition}
c=${#ctype}
a=$((a*2+b+c+d+10))
Чему равно d?