सुरक्षित रूप से एक फ़ाइल बनाएं अगर और केवल अगर यह अजगर के साथ मौजूद नहीं है


93

मैं उस फ़ाइल के आधार पर एक फ़ाइल में लिखना चाहता हूं कि क्या फ़ाइल पहले से मौजूद है या नहीं, केवल यह लिखना कि क्या यह पहले से मौजूद नहीं है (व्यवहार में, मैं फ़ाइलों को तब तक रखने की इच्छा रखता हूं जब तक कि मुझे कोई मौजूद न हो)।

निम्न कोड एक तरीका दिखाता है जिसमें एक संभावित हमलावर एक सिम्लिंक सम्मिलित कर सकता है, जैसा कि इस पोस्ट में फ़ाइल और लिखित फ़ाइल के लिए एक परीक्षण के बीच में सुझाया गया है। यदि कोड उच्च अनुमतियों के साथ चलाया जाता है, तो यह एक मनमानी फ़ाइल को अधिलेखित कर सकता है।

क्या इस समस्या के समाधान का कोई तरीका है?

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')

अजगर के साथ परमाणु लेखन की जाँच करें stackoverflow.com/questions/2333872/…
— मिको ओकटामा

@ मायको यहाँ मदद नहीं करता है।
— कोनराड रुडोल्फ

आह अच्छा। मैं समझ गया कि क्या बात है ... आप केवल लिखते हैं कि क्या फाइल मौजूद है?
— मिको ओक्टमा

क्या आप फ़ाइल को अस्थायी स्थान पर लिख सकते हैं, फिर एक कॉपी कमांड को अधिलेखित किए बिना अनुमति दें?
— एरिक

जवाबों:


93

संपादित करें : डेव जोन्स का जवाब भी देखें : पायथन 3.3 से, आप इस फ़ंक्शन को प्रदान xकरने के open()लिए ध्वज का उपयोग कर सकते हैं ।

नीचे मूल उत्तर

हां, लेकिन पायथन के मानक open()कॉल का उपयोग नहीं कर रहा है । आपको os.open()इसके बजाय उपयोग करने की आवश्यकता होगी , जो आपको अंतर्निहित सी कोड को झंडे निर्दिष्ट करने की अनुमति देता है।

विशेष रूप से, आप उपयोग करना चाहते हैं O_CREAT | O_EXCL। मेरे यूनिक्स सिस्टम के open(2)तहत मैन पेज से O_EXCL:

सुनिश्चित करें कि यह कॉल फ़ाइल बनाता है: यदि यह ध्वज के साथ संयोजन में निर्दिष्ट किया गया है O_CREAT, और pathname पहले से मौजूद है, तो open()विफल हो जाएगा। निर्दिष्ट नहीं होने पर व्यवहार O_EXCLअपरिभाषित O_CREATहै।

जब इन दो झंडों को निर्दिष्ट किया जाता है, तो प्रतीकात्मक लिंक का पालन नहीं किया जाता है: यदि पथनाम एक प्रतीकात्मक लिंक है, तो यह इस बात की open()परवाह किए बिना कि प्रतीकात्मक लिंक कहाँ इंगित करता है।

O_EXCL NFSv3 का उपयोग करते समय या बाद में कर्नेल 2.6 या बाद में NFS पर समर्थित है। ऐसे वातावरण में जहां NFS O_EXCLसमर्थन प्रदान नहीं किया जाता है, प्रोग्राम जो लॉकिंग कार्यों को करने के लिए उस पर भरोसा करते हैं, उनमें एक दौड़ की स्थिति होगी।

तो यह सही नहीं है, लेकिन AFAIK यह निकटतम है आप इस दौड़ की स्थिति से बचने के लिए प्राप्त कर सकते हैं।

संपादित करें: अभी भी लागू होने के os.open()बजाय उपयोग करने के अन्य नियम open()। विशेष रूप से, आप पढ़ने या लिखने के लिए लौट आए फ़ाइल वर्णनकर्ता का उपयोग करना चाहते हैं, आप में से एक की आवश्यकता होगी O_RDONLY, O_WRONLYया O_RDWRझंडे के रूप में अच्छी तरह से।

सभी O_*झंडे पायथन के osमॉड्यूल में हैं, इसलिए आपको आदि का import osउपयोग करना होगा os.O_CREAT।

उदाहरण:

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!")

1
स्पष्ट रूप से सही उत्तर के लिए +1। मैं व्यक्तिगत रूप से यह जानने के लिए उत्सुक हूं कि कितने लोगों के पास वास्तव में एनएफएस कैविएट के साथ मुद्दे हैं - मैं (शायद लापरवाही से) इसे एक अप्रचलित वातावरण के रूप में खारिज करता हूं मेरे कोड को कभी भी नहीं चलाया जाना चाहिए।
— जिग

2
@ ज़िग: एनएफएसवी 3 1995 से है, इसलिए पुराने संस्करणों को अप्रचलित मानना ​​उचित है।
— फ्रेड फू

1
मैं व्यक्तिगत रूप से कर्नेल संस्करण के बारे में अधिक चिंतित हूं। यदि आप अप-टू-डेट सिस्टम के समान कुछ भी चला रहे हैं, तो आपके पास कोई मुद्दा नहीं होना चाहिए, लेकिन RHEL 3 (अभी भी विस्तारित समर्थन चरण में) 2.4 कर्नेल चला रहा है, उदाहरण के लिए। इसके अलावा, मैंने जांच नहीं की है कि क्या वे एफएटी या एनटीएफएस पर विंडोज पर परमाणु लिखते हैं, जो एक संभावित प्रमुख सीमा है।
— me_and

1
@me_and खुले ध्वज स्थिरांक पर अजगर पृष्ठ बताता है कि यह विंडोज के साथ ठीक काम करता है। मैं जल्द ही कोशिश कर रहा हूँ!
— हेनरी गोमर्सल

1
यह सच है, लेकिन मैंने कहीं भी नहीं देखा है ( MSDN सहित ) जो स्पष्ट रूप से कहता है कि ये झंडे परमाणु फ़ाइल निर्माण देते हैं। संभवत: मैं अत्यधिक पागल हो रहा हूं, लेकिन मैं सुरक्षा-महत्वपूर्ण किसी भी चीज के लिए इस पर भरोसा करने से पहले उस "परमाणु" कीवर्ड को देखना चाहता हूं।
— मेरे_और

69

संदर्भ के लिए, पायथन 3.3 इस उपयोग-मामले को कवर करने के 'x'लिए open()फ़ंक्शन में एक नया मोड लागू करता है (केवल मौजूद है, यदि फ़ाइल मौजूद है तो विफल)। ध्यान दें कि 'x'मोड अपने आप निर्दिष्ट है। 'wx'परिणाम के ValueErrorरूप में उपयोग करना 'w'निरर्थक है (केवल एक चीज आप कर सकते हैं यदि कॉल सफल होता है, वैसे भी फ़ाइल में लिखा जाता है; कॉल के सफल होने पर इसका अस्तित्व नहीं हो सकता है):

>>> f1 = open('new_binary_file', 'xb')
>>> f2 = open('new_text_file', 'x')

पायथन 3.2 और नीचे (पायथन 2.x सहित) के लिए कृपया स्वीकृत उत्तर को देखें ।


अच्छा सुझाव। दुर्भाग्य से यह POSIX-only (विंडोज पर काम नहीं करता है) प्रतीत होता है: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'
— Dan Lenski

5
आप अजगर 3.2 का उपयोग कर रहे हैं; 'x' मोड 3.3 और इसके बाद के संस्करण में है, लेकिन यह क्रॉस प्लेटफॉर्म है। संयोग से, आप केवल 'x' के बजाय 'x' का उपयोग करते हैं - लेखन मोड बेमानी है क्योंकि फ़ाइल के साथ आप केवल इतना ही कर सकते हैं, वैसे भी इसे लिखें
— डेव जोन्स

पायथन 3.6:ValueError: must have exactly one of create/read/write/append mode
— ज़ाबोलक्स डोंबी

1
करेंगे - हालाँकि इसके लिए थोड़ी देर बाद कंप्यूटर के सामने आने तक इंतज़ार करना होगा।
— डेव जोन्स

2
लिखने के लिए एक मौजूदा फ़ाइल खोलना उचित है, लेकिन 'x' मोड का पूरा बिंदु फ़ाइल को खोलने के लिए है यदि और केवल अगर यह पहले से मौजूद नहीं है , तो फ़ाइल मौजूद होने पर एक त्रुटि के साथ विफल हो जाती है। यही कारण है कि यह 'w' ध्वज के साथ बेमानी है; अगर यह सफल होता है तो फ़ाइल खाली होने की गारंटी दी जाती है (और इसलिए इसमें पढ़ने की बहुत कम बात है :)।
— डेव जोन्स

0

यदि कोई मौजूद नहीं है तो यह कोड आसानी से एक फ़ाइल बना देगा।

import os
if not os.path.exists('file'):
    open('file', 'w').close() 

15
हाँ यह होगा। प्रश्न के बारे में महत्वपूर्ण बिंदु सुरक्षा पहलू था। समस्या यह है कि फ़ाइल की उपस्थिति की पहचान करने और इसका उपयोग करने या इसे बनाने के बीच, कुछ ऐसा हो सकता है जिससे परिणाम खराब हो (मूल प्रश्न के अनुसार)।
— हेनरी गोमर्सल

5
यह सच है। इसे TOCTOU कहा जाता है!
— रेड

यदि ifकथन के बाद कोई अन्य प्रक्रिया बनाता है और फ़ाइल को लिखता है , तो यह कोड फ़ाइल को खाली कर देगा।
— पीटर वुड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.