Chcę pisać do pliku na podstawie tego, czy ten plik już istnieje, czy nie, pisząc tylko wtedy, gdy jeszcze nie istnieje (w praktyce chciałbym próbować dalej, aż znajdę taki, który nie istnieje).
Poniższy kod przedstawia sposób, w jaki potencjalnie atakujący może wstawić łącze symboliczne, zgodnie z sugestią w tym poście, pomiędzy testem pliku a zapisywanym plikiem. Jeśli kod jest uruchamiany z wystarczająco wysokimi uprawnieniami, może to spowodować nadpisanie dowolnego pliku.
Czy jest jakiś sposób na rozwiązanie tego problemu?
import os
import errno
file_to_be_attacked = 'important_file'
with open(file_to_be_attacked, 'w') as f:
f.write('Some important content!\n')
test_file = 'testfile'
try:
with open(test_file) as f: pass
except IOError, e:
# symlink created here
os.symlink(file_to_be_attacked, test_file)
if e.errno != errno.ENOENT:
raise
else:
with open(test_file, 'w') as f:
f.write('Hello, kthxbye!\n')
Odpowiedzi:
Edycja : Zobacz także odpowiedź Dave'a Jonesa : od Pythona 3.3 możesz użyć
x
flagi,open()
aby zapewnić tę funkcję.Oryginalna odpowiedź poniżej
Tak, ale nie używam standardowego
open()
wywołania Pythona . Zamiast tego będziesz musiał użyćos.open()
, co pozwala określić flagi do bazowego kodu C.W szczególności chcesz użyć
O_CREAT | O_EXCL
. Ze strony podręcznika dlaopen(2)
underO_EXCL
w moim systemie Unix:Więc to nie jest idealne, ale AFAIK to najbliższy sposób uniknięcia tego wyścigu.
Edycja: pozostałe zasady używania
os.open()
zamiastopen()
nadal obowiązują. W szczególności, jeśli chcesz użyć zwróconego deskryptora pliku do odczytu lub zapisu, będziesz potrzebować również jednej z flagO_RDONLY
,O_WRONLY
lubO_RDWR
.Wszystkie
O_*
flagi znajdują się wos
module Pythona , więc musiszimport os
i używaćos.O_CREAT
itp.Przykład:
import os import errno flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY try: file_handle = os.open('filename', flags) except OSError as e: if e.errno == errno.EEXIST: # Failed as the file already exists. pass else: # Something unexpected went wrong so reraise the exception. raise else: # No exception, so the file must have been created successfully. with os.fdopen(file_handle, 'w') as file_obj: # Using `os.fdopen` converts the handle to an object that acts like a # regular Python file object, and the `with` context manager means the # file will be automatically closed when we're done with it. file_obj.write("Look, ma, I'm writing to a new file!")
źródło
Dla porównania, Python 3.3 implementuje nowy
'x'
tryb wopen()
funkcji, aby objąć ten przypadek użycia (tylko tworzenie, niepowodzenie, jeśli plik istnieje). Zauważ, że'x'
tryb jest określony samodzielnie. Używanie'wx'
wyników w aValueError
jako, że'w'
jest nadmiarowe (jedyną rzeczą, którą możesz zrobić, jeśli wywołanie się powiedzie, jest i tak zapis do pliku; nie może istnieć, jeśli wywołanie się powiedzie):>>> f1 = open('new_binary_file', 'xb') >>> f2 = open('new_text_file', 'x')
W przypadku Pythona 3.2 i starszych (w tym Python 2.x) prosimy zapoznać się z zaakceptowaną odpowiedzią .
źródło
Python 3.2 (r32:88445, Feb 20 2011, 21:30:00)
[MSC v.1500 64 bit (AMD64)] on win32
>>> open("c:/temp/foo.csv","wx")
ValueError: invalid mode: 'wx'
ValueError: must have exactly one of create/read/write/append mode
Ten kod z łatwością utworzy PLIK, jeśli taki nie istnieje.
import os if not os.path.exists('file'): open('file', 'w').close()
źródło
if
instrukcji, ten kod spowoduje wyczyszczenie pliku.