Немного черной магии: bash, cgi и загрузка файлов через POST

Прежде, чем меня объявят съехавшим с катушек фриком, оговорюсь, что это делалось для борды, где наличествует всего 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 в браузере… Почему бы и нет. Только не забудьте пристрелить того, кто это решит использовать на боевом сервере в продакшне.

Немного черной магии: bash, cgi и загрузка файлов через POST: 1 комментарий

Добавить комментарий