app/django/contrib/sessions/backends/file.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 import errno
     1 import os
     2 import os
     2 import tempfile
     3 import tempfile
       
     4 
     3 from django.conf import settings
     5 from django.conf import settings
     4 from django.contrib.sessions.backends.base import SessionBase
     6 from django.contrib.sessions.backends.base import SessionBase, CreateError
     5 from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
     7 from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
       
     8 
     6 
     9 
     7 class SessionStore(SessionBase):
    10 class SessionStore(SessionBase):
     8     """
    11     """
     9     Implements a file based session store.
    12     Implements a file based session store.
    10     """
    13     """
    13         if not self.storage_path:
    16         if not self.storage_path:
    14             self.storage_path = tempfile.gettempdir()
    17             self.storage_path = tempfile.gettempdir()
    15 
    18 
    16         # Make sure the storage path is valid.
    19         # Make sure the storage path is valid.
    17         if not os.path.isdir(self.storage_path):
    20         if not os.path.isdir(self.storage_path):
    18             raise ImproperlyConfigured("The session storage path %r doesn't exist. "\
    21             raise ImproperlyConfigured(
    19                                        "Please set your SESSION_FILE_PATH setting "\
    22                 "The session storage path %r doesn't exist. Please set your"
    20                                        "to an existing directory in which Django "\
    23                 " SESSION_FILE_PATH setting to an existing directory in which"
    21                                        "can store session data." % self.storage_path)
    24                 " Django can store session data." % self.storage_path)
    22 
    25 
    23         self.file_prefix = settings.SESSION_COOKIE_NAME
    26         self.file_prefix = settings.SESSION_COOKIE_NAME
    24         super(SessionStore, self).__init__(session_key)
    27         super(SessionStore, self).__init__(session_key)
    25 
    28 
    26     def _key_to_file(self, session_key=None):
    29     def _key_to_file(self, session_key=None):
    29         """
    32         """
    30         if session_key is None:
    33         if session_key is None:
    31             session_key = self.session_key
    34             session_key = self.session_key
    32 
    35 
    33         # Make sure we're not vulnerable to directory traversal. Session keys
    36         # Make sure we're not vulnerable to directory traversal. Session keys
    34         # should always be md5s, so they should never contain directory components.
    37         # should always be md5s, so they should never contain directory
       
    38         # components.
    35         if os.path.sep in session_key:
    39         if os.path.sep in session_key:
    36             raise SuspiciousOperation("Invalid characters (directory components) in session key")
    40             raise SuspiciousOperation(
       
    41                 "Invalid characters (directory components) in session key")
    37 
    42 
    38         return os.path.join(self.storage_path, self.file_prefix + session_key)
    43         return os.path.join(self.storage_path, self.file_prefix + session_key)
    39 
    44 
    40     def load(self):
    45     def load(self):
    41         session_data = {}
    46         session_data = {}
    42         try:
    47         try:
    43             session_file = open(self._key_to_file(), "rb")
    48             session_file = open(self._key_to_file(), "rb")
    44             try:
    49             try:
    45                 try:
    50                 file_data = session_file.read()
    46                     session_data = self.decode(session_file.read())
    51                 # Don't fail if there is no data in the session file.
    47                 except(EOFError, SuspiciousOperation):
    52                 # We may have opened the empty placeholder file.
    48                     self._session_key = self._get_new_session_key()
    53                 if file_data:
    49                     self._session_cache = {}
    54                     try:
    50                     self.save()
    55                         session_data = self.decode(file_data)
    51                     # Ensure the user is notified via a new cookie.
    56                     except (EOFError, SuspiciousOperation):
    52                     self.modified = True
    57                         self.create()
    53             finally:
    58             finally:
    54                 session_file.close()
    59                 session_file.close()
    55         except(IOError):
    60         except IOError:
    56             pass
    61             pass
    57         return session_data
    62         return session_data
    58 
    63 
    59     def save(self):
    64     def create(self):
       
    65         while True:
       
    66             self._session_key = self._get_new_session_key()
       
    67             try:
       
    68                 self.save(must_create=True)
       
    69             except CreateError:
       
    70                 continue
       
    71             self.modified = True
       
    72             self._session_cache = {}
       
    73             return
       
    74 
       
    75     def save(self, must_create=False):
       
    76         # Get the session data now, before we start messing
       
    77         # with the file it is stored within.
       
    78         session_data = self._get_session(no_load=must_create)
       
    79 
       
    80         session_file_name = self._key_to_file()
       
    81 
    60         try:
    82         try:
    61             f = open(self._key_to_file(self.session_key), "wb")
    83             # Make sure the file exists.  If it does not already exist, an
       
    84             # empty placeholder file is created.
       
    85             flags = os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0)
       
    86             if must_create:
       
    87                 flags |= os.O_EXCL
       
    88             fd = os.open(session_file_name, flags)
       
    89             os.close(fd)
       
    90 
       
    91         except OSError, e:
       
    92             if must_create and e.errno == errno.EEXIST:
       
    93                 raise CreateError
       
    94             raise
       
    95 
       
    96         # Write the session file without interfering with other threads
       
    97         # or processes.  By writing to an atomically generated temporary
       
    98         # file and then using the atomic os.rename() to make the complete
       
    99         # file visible, we avoid having to lock the session file, while
       
   100         # still maintaining its integrity.
       
   101         #
       
   102         # Note: Locking the session file was explored, but rejected in part
       
   103         # because in order to be atomic and cross-platform, it required a
       
   104         # long-lived lock file for each session, doubling the number of
       
   105         # files in the session storage directory at any given time.  This
       
   106         # rename solution is cleaner and avoids any additional overhead
       
   107         # when reading the session data, which is the more common case
       
   108         # unless SESSION_SAVE_EVERY_REQUEST = True.
       
   109         #
       
   110         # See ticket #8616.
       
   111         dir, prefix = os.path.split(session_file_name)
       
   112 
       
   113         try:
       
   114             output_file_fd, output_file_name = tempfile.mkstemp(dir=dir,
       
   115                 prefix=prefix + '_out_')
       
   116             renamed = False
    62             try:
   117             try:
    63                 f.write(self.encode(self._session))
   118                 try:
       
   119                     os.write(output_file_fd, self.encode(session_data))
       
   120                 finally:
       
   121                     os.close(output_file_fd)
       
   122                 os.rename(output_file_name, session_file_name)
       
   123                 renamed = True
    64             finally:
   124             finally:
    65                 f.close()
   125                 if not renamed:
    66         except(IOError, EOFError):
   126                     os.unlink(output_file_name)
       
   127 
       
   128         except (OSError, IOError, EOFError):
    67             pass
   129             pass
    68 
   130 
    69     def exists(self, session_key):
   131     def exists(self, session_key):
    70         if os.path.exists(self._key_to_file(session_key)):
   132         if os.path.exists(self._key_to_file(session_key)):
    71             return True
   133             return True
    72         return False
   134         return False
    73 
   135 
    74     def delete(self, session_key):
   136     def delete(self, session_key=None):
       
   137         if session_key is None:
       
   138             if self._session_key is None:
       
   139                 return
       
   140             session_key = self._session_key
    75         try:
   141         try:
    76             os.unlink(self._key_to_file(session_key))
   142             os.unlink(self._key_to_file(session_key))
    77         except OSError:
   143         except OSError:
    78             pass
   144             pass
    79 
   145