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 |