Bug#655154: woof: No space left on device when /tmp is full

2014-02-13 Thread Florent Rougon
tags 655154 + upstream
forwarded 655154 si...@budig.de
thanks

Hello,

The attached patch fixes this problem. I have forwarded it to the
upstream maintainer. Here follows the patch description:

,
| Improve handling of files received with option -U
| 
| Instead of storing the downloaded contents into a temporary file
| that is copied by woof to the final destination (current directory),
| the uploaded file is directly written to the current directory, with
| precautions to avoid overwriting existing files.
| 
| This is done by subclassing cgi.FieldStorage to override make_file() so
| that it does not create a temporary file when encountering an atomic
| part whose name is "upfile" in the MIME data.
| 
| This improvement is particularly welcome when receiving large files,
| since it avoids:
|   - a possible failure due to the temporary directory being too small to
| hold the file;
|   - a useless copy of the whole file;
|   - a possible loss of the whole download in case a failure occurs when
| woof copies the (complete) temporary file to its final destination.
`

For people who don't want to apply the patch, setting TMPDIR allows a
partial workaround: you may indicate a directory with more space than
/tmp this way, but you will still have to hold two copies of the
downloaded file at the same time (one in TMPDIR and the other in the
directory from which 'woof -U' was started), until woof deletes the
temporary file through garbage collection. Depending on the file size
and the available space, this may or may not be possible.

-- 
Florent
diff --git a/woof b/woof
index 2abedf0..fde8de1 100755
--- a/woof
+++ b/woof
@@ -124,6 +124,64 @@ class ForkingHTTPServer (BaseHTTPServer.HTTPServer):
   self.close_request (request)
 
 
+class MyFieldStorage (cgi.FieldStorage):
+   """Subclass of FieldStorage with efficient handling of uploaded files.
+
+   This class behaves the same as FieldStorage except that it handles uploaded
+   files more efficiently: instead of storing the data into a temporary file
+   that is copied by woof to the final destination (current directory),
+   the uploaded file is directly written to the current directory, with
+   precautions to avoid overwriting existing files.
+
+   """
+   # The function signature changed between Python 2 and Python 3; this will
+   # work in all cases.
+   def make_file(self, *args, **kwargs):
+  # The "self.list is None" check is here to prevent misbehaving in case we
+  # receive a multipart whose name happens to be "upfile".
+  if self.list is None and self.name == "upfile":
+ # make_file() was called for an uploaded file. No need to create a
+ # potentially huge temporary file only to copy it to the final
+ # destination. Just create the file in a way that avoids overwriting
+ # existing files.
+ upfilename = self.filename
+
+ if "\\" in upfilename:
+upfilename = upfilename.split ("\\")[-1]
+
+ upfilename = os.path.basename (self.filename)
+
+ destfile = None
+ for suffix in ["", ".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8",
+".9"]:
+destfilename = os.path.join (upfilename + suffix)
+try:
+   destfile = os.open (destfilename,
+   os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
+   break
+except OSError, e:
+   if e.errno == errno.EEXIST:
+  continue
+   raise
+
+ # Fallback to tempfile.mkstemp() in case the above did not manage to
+ # create the file.
+ if destfile is None:
+destfile, destfilepath = tempfile.mkstemp (
+   prefix = upfilename + ".", dir = ".")
+# 'destfilepath' is a full path, be consistent with the normal case
+destfilename = os.path.basename (destfilepath)
+
+ msg = "Accepting uploaded file: '%s'" % upfilename
+ if upfilename != destfilename:
+msg += ", stored as '%s'" % destfilename
+ sys.stderr.write (msg + "...\n")
+
+ return os.fdopen (destfile, "wb")
+  else:
+ return cgi.FieldStorage.make_file (self, *args, **kwargs)
+
+
 # Main class implementing an HTTP-Requesthandler, that serves just a single
 # file and redirects all other requests to this file (this passes the actual
 # filename to the client).
@@ -152,49 +210,26 @@ class FileServHTTPRequestHandler (BaseHTTPServer.BaseHTTPRequestHandler):
   # http://mail.python.org/pipermail/python-list/2006-September/402441.html
 
   ctype, pdict = cgi.parse_header (self.headers.getheader ('Content-Type'))
-  form = cgi.FieldStorage (fp = self.rfile,
-   headers = self.headers,
-   environ = {'REQUEST_METHOD' : 'POST'},
-   keep_blank_values = 1,
-   strict_parsing = 1)
+  fo

Bug#655154: woof: No space left on device when /tmp is full

2012-02-09 Thread Gasparini Andrea
Hi Arne,
thanks for the report, and sorry for delay as well.

The problem you raise, is matter of how the python standard library works.
I guess the problem can be ignored at all (probably writing something
smarter instead of the complete traceback), or an option can be added
to specify the temp dir while uploading.

I'll bring this to the upstream (not responsive at all, frankly) and
to a couple of other people already helped with woof.

Thanks again.
-- 
- Andrea Gasparini -



-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org



Bug#655154: woof: No space left on device when /tmp is full

2012-01-08 Thread Arne Wichmann
Package: woof
Version: 20091227-2
Severity: normal

Dear Maintainer,

today woof threw exceptions while receiving an uploading file.

   * What led up to the situation?

While using the upload-function, uploading to a machine with a 50MB
/tmp (my router) i encountered "No space left on device" exception. 
woof was started in a directory where enough space was provided for
the expected data and (smaller) testuploads succeeded.

   * What was the outcome of this action?

Exception happened during processing of request from ('__.__.__.__', 49201)
Traceback (most recent call last):
  File "/usr/lib/python2.6/SocketServer.py", line 560, in process_request_thread
self.finish_request(request, client_address)
  File "/usr/lib/python2.6/SocketServer.py", line 322, in finish_request
self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.6/SocketServer.py", line 617, in __init__
self.handle()
  File "/usr/lib/python2.6/BaseHTTPServer.py", line 329, in handle
self.handle_one_request()
  File "/usr/lib/python2.6/BaseHTTPServer.py", line 323, in handle_one_request
method()
  File "/usr/bin/woof", line 153, in do_POST
strict_parsing = 1)
  File "/usr/lib/python2.6/cgi.py", line 508, in __init__
self.read_multi(environ, keep_blank_values, strict_parsing)
  File "/usr/lib/python2.6/cgi.py", line 637, in read_multi
environ, keep_blank_values, strict_parsing)
  File "/usr/lib/python2.6/cgi.py", line 510, in __init__
self.read_single()
  File "/usr/lib/python2.6/cgi.py", line 647, in read_single
self.read_lines()
  File "/usr/lib/python2.6/cgi.py", line 669, in read_lines
self.read_lines_to_outerboundary()
  File "/usr/lib/python2.6/cgi.py", line 720, in read_lines_to_outerboundary
self.__write(odelim + line)
  File "/usr/lib/python2.6/cgi.py", line 679, in __write
self.file.write(line)
IOError: [Errno 28] No space left on device

   * What outcome did you expect instead?

I would have expected that woof does not use /tmp to cache large files. A cache 
in the destination directory seems more appropriate.

Yours,
arne wichmann


-- System Information:
Debian Release: wheezy/sid
  APT prefers testing
  APT policy: (990, 'testing'), (500, 'unstable'), (500, 'stable'), (500, 
'oldstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 3.1.0-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=C, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages woof depends on:
ii  python  2.6.7-3

woof recommends no packages.

woof suggests no packages.

-- no debconf information



-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org