Perl: Uber uploader и проблема «Failed to assign CGI temp directory: Inappropriate ioctl for device»

Довелось мне недавно переносить один очень старый проект на новый сервер. Естественно повылазило куча проблем. Половина методов в более свежих версиях PHP стали устаревшими, какие то механизмы стали работать по другому. Но все это решается достаточно просто и быстро. Но вот с одной проблемой мне пришлось повозится как следует… Вот о ней я и ходу сегодня рассказать…

В проекте, который мне стоило перенести, использовалось решение Uber uploader предназначенное для загрузки файлов на сервер через Ajax. Хорошее и достаточно распространенное решение, которое ни в какую не захотело работать на новом сервере. Суть проблемы заключалась в том, что при отправки файла на perl скрипт ubr_upload.pl он падал с кодом ошибки 500 и следующим содержанием в логах апача: «Failed to assign CGI temp directory: Inappropriate ioctl for device«.

[cgi:error] [pid 19699] AH01215: ubr_upload.pl: private_tempfiles has been deprecated at /usr/share/perl5/CGI.pm line 3214.: .../upload/cgi-bin/ubr_upload.pl
[cgi:error] [pid 19699] AH01215: ubr_upload.pl: Failed to assign CGI temp directory: Inappropriate ioctl for device at .../upload/cgi-bin/ubr_upload.pl line 166.: .../upload/cgi-bin/ubr_upload.pl

Честно в перле я полный ноль. Но по тексту мне стало понятно, что дело, скорей всего, в правах доступа. Я убедился что на нужные мне папки установлены права 755 и стал смотреть настройки старого сервера. В них я тоже ничего не нашел. Гугл навел меня на мысли, что такое поведение похоже на срабатывание mod_security в апаче. А в качестве решения было предложено создать файл .htaccess в папке cgi-bin скрипта следующего вида:

<IfModule mod_security.c>  
  SecRuleEngine Off
  SecFilterInheritance Off
  SecFilterEngine Off
  SecFilterScanPOST Off
  SecRuleRemoveById 300015 3000016 3000017
</IfModule>

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

Из логов понятно в каких строчках вылазиют ошибки.

# Disable private temp files
CGI::private_tempfiles(0);

# Tell CGI.pm to use our directory based on upload id
if($TempFile::TMPDIRECTORY){ $TempFile::TMPDIRECTORY = $temp_dir_id; }
elsif($CGITempFile::TMPDIRECTORY){ $CGITempFile::TMPDIRECTORY = $temp_dir_id; }
else{ die("Failed to assign CGI temp directory: $!"); }

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

#CGI::private_tempfiles(0);

Далее пошли мучительные разборы полетов, которые меня привели к следующему решению:

1) В начале файла (после use FileHandle;) добавляем строчку:

use FileHandle;
use File::Temp qw/tempdir/; #NEW LINE (add module)

2) Вместо блока с ошибкой вставляем следующую строку:

# Disable private temp files
#CGI::private_tempfiles(0);

# Tell CGI.pm to use our directory based on upload id
#if($TempFile::TMPDIRECTORY){ $TempFile::TMPDIRECTORY = $temp_dir_id; }
#elsif($CGITempFile::TMPDIRECTORY){ $CGITempFile::TMPDIRECTORY = $temp_dir_id; }
#else{ die("Failed to assign CGI temp directory: $!"); }

tempdir (DIR => $temp_dir_id); #NEW LINE (funtion from module File::Temp)

3) Ищем строку my @post_values = $query->param($key); и заменяем ее на следующую:

foreach my $key (@names){
   #my @post_values = $query->param($key);
   my @post_values = $query->multi_param($key); //NEW LINE

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

Полную версию скрипта с правками Вы можете скачать по данной ссылке.

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