2010-07-11 15 views
6

मैं B को A नाम बदलना चाहते हैं, लेकिन केवल तभी B मौजूद नहीं है, भोली बात करता है, तो B मौजूद है (access("B", F_OK) या ऐसा ही कुछ के साथ) की जाँच की जाएगी, और अगर यह rename साथ आगे बढ़ने से नहीं करता है। दुर्भाग्यवश यह एक खिड़की खुलती है जिसके दौरान कुछ अन्य प्रक्रिया B बनाने का निर्णय ले सकती है, और फिर यह ओवरराइट हो जाती है - और इससे भी बदतर कोई संकेत नहीं है कि ऐसा कुछ भी हुआ।दौड़ की स्थिति के बिना() का नाम कैसे बदलें?

अन्य फाइल सिस्टम का उपयोग कर सकते कार्यों इस से ग्रस्त नहीं है - openO_EXCL (ताकि फ़ाइलों की प्रतिलिपि बनाने के लिए सुरक्षित है), और हाल ही में लिनक्स *at syscalls कि अधिकांश अन्य दौड़ की स्थिति के खिलाफ की रक्षा के एक पूरे परिवार मिल गया है - लेकिन यह विशेष रूप से एक (renameat मौजूद है, लेकिन एक पूरी तरह से अलग समस्या के खिलाफ सुरक्षा करता है)।

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

+0

शायद आपको एक लंबित लॉकिंग (एक रैपर) नाम बदलने के बजाय एक स्पष्ट लॉकिंग तंत्र का उपयोग करने पर विचार करना चाहिए। यदि 'बी' बनाता है जो आपके नियंत्रण में एक प्रोग्राम है, तो आप संभवतः इंटरप्रोसेस सिंक्रनाइज़ेशन प्राइमेटिव का उपयोग कर सकते हैं। –

उत्तर

14

आपको link (2) को नए फ़ाइल नाम में सक्षम होना चाहिए। यदि लिंक विफल रहता है तो आप हार मानते हैं क्योंकि फ़ाइल पहले से मौजूद है। यदि लिंक सफल होता है, तो आपकी फ़ाइल अब पुराने और नए नाम दोनों के अंतर्गत मौजूद है। फिर आप unlink (2) पुराना नाम। कोई संभावित दौड़ की स्थिति नहीं है।

+0

फाइलें विभिन्न माउंट पॉइंट्स के नीचे क्या हैं? –

+2

@ मॉरॉन: नाम बदलें() विभिन्न फाइल सिस्टम में काम नहीं करता है। – Dummy00001

+0

@ डमी 00001: मैं देखता हूं। मैं शीर्षक में ब्रैकेट याद किया। +1 तब :-) –

1

नाम बदलने आदमी पृष्ठ से:

Newpath पहले से ही मौजूद है, तो यह atomically (कुछ शर्तों के अधीन, नीचे दी गई त्रुटियां देखें) बदल दिया जाएगा, ताकि वहाँ कोई मतलब नहीं है जो एक और पर न्यूपैथ तक पहुंचने का प्रयास करने की प्रक्रिया में यह गुम हो जाएगा।

तो B फ़ाइल पहले से मौजूद होने पर नाम बदलने से बचना संभव नहीं है। मुझे लगता है कि यदि आपके पास नाम बदलने का प्रयास करने से पहले, यदि आप पहले से मौजूद हैं तो फ़ाइल का नाम नहीं होना चाहिए, तो शायद आपके पास अस्तित्व की जांच करने के लिए कोई विकल्प नहीं है (stat()access() का उपयोग करें)। दौड़ की स्थिति को नजरअंदाज करना

अन्यथा, लिंक() के साथ नीचे दिया गया समाधान आपकी आवश्यकताओं के अनुरूप लगता है।

+0

यदि फ़ाइल बी मौजूद नहीं है, तो यदि आप अस्तित्व के लिए परीक्षण करते हैं तो अभी भी दौड़ की स्थिति है - दोनों प्रक्रियाओं का पता लगाया जा सकता है कि यह वहां नहीं है, और फिर दोनों नाम बदलते हैं, अनिश्चित परिणामों के साथ, जिस प्रक्रिया के नाम पर अंतिम नाम बदल दिया गया है। एकमात्र समाधान नाम() को परमाणु होने के लिए है और विफल होने के लिए यदि नया फ़ाइल नाम पहले से मौजूद है, जो विंडोज पर यह करता है, और जो मैंने सोचा (गलत लगता है) सी मानक निर्दिष्ट है। –

6

आप मौजूदा फ़ाइल में link() मौजूदा फ़ाइल में अपने इच्छित फ़ाइल नाम के साथ कर सकते हैं, तो मौजूदा फ़ाइल नाम हटा दें।

link() केवल नया लिंक बनाने में सफल होना चाहिए यदि नया पथनाम पहले से मौजूद नहीं है।

कुछ की तरह:

int result = link("A", "B"); 

if (result != 0) { 
    // the link wasn't created for some reason (maybe because "B" already existed) 
    // handle the failure however appropriate... 
    return -1; 
} 

// at this point there are 2 filenames hardlinked to the contents of "A", 
// filename "A" and filename "B" 

// remove filename "A" 
unlink("A"); 

इस तकनीक (पासवर्ड फ़ाइल को संशोधित करने के बारे में चर्चा देखें) link() के लिए डॉक्स में चर्चा की है:

3

जोड़ने के लिए खेद है पुराने धागे के लिए कुछ। और इतनी लंबी पोस्ट करने के लिए।

मैं केवल ताला जो लगभग intermittend सर्वर बूट के साथ किसी भी फाइल सिस्टम पर काम करना चाहिए, यहां तक ​​कि एनएफएस पर और ग्राहक समय जगह में warps के अभाव में पूरी तरह से रेस स्थिति मुक्त rename() करने के लिए एक ही रास्ता पता।

निम्नलिखित नुस्खा दौड़ की स्थिति मुक्त है इस अर्थ में कि किसी भी परिस्थिति में डेटा खो नहीं जा सकता है। इसे ताले की भी आवश्यकता नहीं है और उन ग्राहकों द्वारा किया जा सकता है जो सहयोग नहीं करना चाहते हैं सिवाय इसके कि वे सभी एक ही एल्गोरिदम का उपयोग करते हैं।

यह इस शर्त में मुक्त स्थिति नहीं है कि, अगर कुछ गंभीरता से टूट जाता है, तो सब कुछ साफ-सुथरा राज्य में छोड़ दिया जाता है। इसमें थोड़ी सी अवधि भी है, जहां न तो स्रोत और न ही गंतव्य उनके स्थान पर मौजूद हैं, हालांकि स्रोत अभी भी किसी अन्य नाम के आसपास है। और यह उन मामलों के खिलाफ कठोर नहीं है जहां एक हमलावर नुकसान को उकसाता है (rename() अपराधी है, आंकड़े जाओ)।

एस स्रोत है, डी गंतव्य, पी (एक्स) dirname(x) है, सी (एक्स, वाई) x/y पथ संयोजन

  1. जाँच लें कि गंतव्य मौजूद नहीं है। बस यह सुनिश्चित करने के लिए कि हम व्यर्थ में अगले कदम नहीं करते हैं। = सी (पी (डी), यादृच्छिक)
  2. mkdir (टी), अगर यह पिछले चरण
  3. खुला (सी (टी करने के लिए लूप विफल रहता है, "ताला"), O_EXCL:
  4. एक शायद अनूठा नाम टी बनाने), इस rmdir विफल रहता है (टी) पिछले चरण
  5. नाम बदलने (एस, सी (टी, "tmp" करने के लिए त्रुटियों और पाश अनदेखी))
  6. लिंक (सी (टी, "tmp"), डी)
  7. अनलिंक (सी (टी, "tmp"))
  8. अनलिंक (सी (टी, "ताला"))
  9. rmdir (टी)

एल्गोरिथ्म safe_rename(S,D) समझाया:

समस्या यह है कि हम यकीन है कि वहाँ कोई रेस स्थिति है, न तो स्रोत पर और न ही गंतव्य पर बनाना चाहते हैं। यह माना जाता है कि, (लगभग) प्रत्येक चरण के बीच कुछ भी हो सकता है, लेकिन अन्य सभी प्रक्रियाएं दौड़ की स्थिति मुक्त नाम करते समय सटीक उसी एल्गोरिदम का पालन करती हैं। इसमें यह भी शामिल है कि अस्थायी निर्देशिका टी को कभी भी छुआ नहीं जाता है, यह सुनिश्चित करने के अलावा (यह एक मैन्युअल प्रक्रिया है) कि निर्देशिका का उपयोग करने की प्रक्रिया मर गई है और पुनरुत्थान नहीं किया जा सकता है (जैसे पुनर्स्थापना के बाद एक वीएम हाइबरनेट जारी रखना)।

rename() ठीक से करने के लिए, हमें छिपाने के लिए कुछ जगह चाहिए। इसलिए हम एक निर्देशिका को एक तरह से बनाते हैं जो सुनिश्चित करता है कि कोई और (जो एक ही एल्गोरिदम का पालन कर रहा है) गलती से इसका उपयोग करेगा।

हालांकि mkdir() एनएफएस पर परमाणु होने की गारंटी नहीं है। इसलिए हमें यह सुनिश्चित करने की ज़रूरत है कि हमारे पास कुछ गारंटी है कि हम निर्देशिका में अकेले हैं। यह लॉकफाइल पर O_EXCL है। यह है - कड़ाई से बोलना - लॉकिंग नहीं, यह एक सैमफोर है।

ऐसे दुर्लभ मामलों के अलावा, mkdir() आमतौर पर परमाणु है। इसके अलावा हम निर्देशिका के लिए कुछ क्रिप्टोग्राफ़िक रूप से सुरक्षित यादृच्छिक नाम का उपयोग कर सकते हैं, यह सुनिश्चित करने के लिए कुछ GUID, होस्टनाम और पीआईडी ​​जोड़ें कि यह बहुत ही असंभव है कि कोई और मौका से वही नाम चुनता है। हालांकि यह प्रमाणित करने के लिए कि एल्गोरिदम सही है, हमें lock नामक इस फ़ाइल की आवश्यकता है।

अब हमारे पास अधिकतर खाली निर्देशिका है, हम सुरक्षित रूप से rename() स्रोत को सुरक्षित कर सकते हैं। यह सुनिश्चित करता है कि जब तक हम unlink() नहीं करेंगे तब तक कोई भी स्रोत को बदल देता है। (ठीक है, सामग्री बदल सकती है, यह कोई समस्या नहीं है।)

अब link() चाल को यह सुनिश्चित करने के लिए लागू किया जा सकता है कि हम गंतव्य को ओवरराइट नहीं करते हैं।

बाद में unlink() शेष स्रोत पर दौड़ की स्थिति मुक्त किया जा सकता है। बाकी सफाई है।

केवल एक ही समस्या छोड़ दिया है:

मामले में link() विफल रहता है हम पहले से ही स्रोत चले गए हैं। उचित सफाई के लिए हमें इसे वापस ले जाने की जरूरत है। यह safe_rename(C(T,"tmp"),S) पर कॉल करके किया जा सकता है। यदि यह विफल रहता है, तो हम जितना भी कर सकते हैं, उतना ही हम कर सकते हैं जितना हम कर सकते हैं (unlink(C(T,"lock")), rmdir(T)) और व्यवस्थापक द्वारा मैन्युअल क्लीनअप के लिए मलबे को पीछे छोड़ दें।

अंतिम नोट:

मलबे मामले में साफ करने के लिए मदद करने के लिए, आप संभवतः tmp की तुलना में कुछ बेहतर फ़ाइल नाम का उपयोग कर सकते हैं। नाम चुपचाप चुनना कुछ हद तक हमलों के खिलाफ एल्गोरिदम को भी कठोर कर सकता है।

और यदि आप कहीं फ़ाइलों के ट्रेन लोड को स्थानांतरित कर रहे हैं तो आप पाठ्यक्रम की निर्देशिका का पुन: उपयोग कर सकते हैं।

हालांकि, मैं मानता हूं कि यह एल्गोरिदम सादा ओवरकिल है और O_EXCLrename() पर कुछ गुम है।

संबंधित मुद्दे