Прежде, чем меня объявят съехавшим с катушек фриком, оговорюсь, что это делалось для борды, где наличествует всего 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
"
echo "Written to $dir, filename $2 ($dir/$file)
"
dd ibs=1 obs=512 count=$SIZE of=$dir/$file
Вот и все! Ни временных файлов, ни нет необходимости буферизовать весь файл в памяти, как делает PHP. просто один небольшой dd и немного особой магии, для того, чтобы рассчитать откуда и сколько ддшить.
Разумеется, реализация тупа как дуб, ни каких POST переменных формочки, только сам файл. Потому имя и путь для файла я передаю GET’ом, разделенные ‘:’.
В таком виде, это, конечно, дыра в безопасности размером… Не будем говорить каких размеров. Но так как из-за спейифики задачи, мне вообще надо лазить по физической памяти камня из JS в браузере… Почему бы и нет. Только не забудьте пристрелить того, кто это решит использовать на боевом сервере в продакшне.
Leave a Reply