2016-10-26 10 views
6

से अधिक लंबा है, तो मुझे fanotify से फाइल सिस्टम ईवेंट प्राप्त होते हैं। कभी-कभी मैं उस फ़ाइल के लिए एक पूर्ण पथ प्राप्त करना चाहता हूं जिसे एक्सेस किया जा रहा है।पथ वर्णनकर्ता से पथ प्राप्त करें जब पथ PATH_MAX

आमतौर पर, यह एक समस्या नहीं है - तो मैं /proc/self/fd/<fd> पर readlink फोन और मेरे पथ प्राप्त कर सकते हैं fanotify_event_metadata, एक फ़ाइल वर्णनकर्ता fd शामिल हैं।

हालांकि, यदि PATH_MAXreadlink से अधिक पथ का उपयोग नहीं किया जा सकता है - यह ENAMETOOLONG के साथ विफल रहता है। मैं सोच रहा हूं कि इस मामले में फ़ाइल पथ प्राप्त करने का कोई तरीका है या नहीं।

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

मैंने fd को O_PATH के साथ फिर से खोलकर और openat(fd, "..", ...) पर कॉल करके एक मूल निर्देशिका प्राप्त करने का प्रयास किया है। जाहिर है, यह असफल रहा क्योंकि fd किसी निर्देशिका का संदर्भ नहीं देता है। असफल readlink कॉल के बाद मैंने बफर की सामग्री की जांच करने का भी प्रयास किया है (उम्मीद है कि इसमें आंशिक पथ है)। वह या तो काम नहीं किया।

अभी तक मैंने उन्हें खोले गए प्रक्रिया की कार्यशील निर्देशिका के अंदर फ़ाइलों के लिए लंबे पथ प्राप्त करने में कामयाब रहे हैं (फैनोटिफ़ाई ईवेंट में लक्ष्य प्रक्रिया के pid शामिल हैं, इसलिए मैं /proc/<pid>/cwd पढ़ सकता हूं और रूट से पथ प्राप्त कर सकता हूं क्या आप वहां मौजूद हैं)। लेकिन यह एक आंशिक समाधान है।

क्या संपूर्ण फाइल सिस्टम को घुमाने के बिना फ़ाइल डिस्क्रिप्टर से पूर्ण पथ प्राप्त करने का कोई तरीका है? अधिमानतः वह कर्नेल 2.6.32/glibc 2.11 के साथ काम करेगा।

अद्यतन: उत्सुक के लिए। मुझे पता चला है कि readlink("/proc/self/fd/<fd>", ... क्यों बुलाकर पूरे पथ को स्टोर करने के लिए काफी बड़ा काम नहीं करता है।

do_proc_readlink के कार्यान्वयन को देखें। ध्यान दें कि यह सीधे buffer प्रदान नहीं करता है। इसके बजाए, यह एक पृष्ठ को आवंटित करता है और इसे अस्थायी बफर के रूप में उपयोग करता है जब यह d_path पर कॉल करता है। दूसरे शब्दों में, कोई फर्क नहीं पड़ता कि buffer कितना बड़ा है, d_path हमेशा किसी पृष्ठ के आकार तक सीमित रहेगा। Amd64 पर 4096 बाइट्स कौन सा है। PATH_MAX के समान! -ENAMETOOLONG स्वयं prepend द्वारा वापस आ गया है जब यह उल्लिखित पृष्ठ से बाहर हो जाता है।

+0

क्या अंतिम हल किए गए पथ लिंक और/या '.' /' ..' 'PATH_MAX' से अधिक तत्व हैं? यदि नहीं, तो 'realpath() 'आपके लिए काम कर सकता है: http://man7.org/linux/man-pages/man3/realpath.3.html –

+1

पथ नहीं हैं, ठीक है,' PATH_MAX' से छोटा वर्ण? यह अजीब लगता है। – unwind

+0

@AndrewHenle, 'realpath' को पहले स्थान पर एक पथ की आवश्यकता है। मेरे पास एक फाइल डिस्क्रिप्टर और एक पीआईडी ​​है। –

उत्तर

1

readlink एक लिंक लक्ष्य PATH_MAX से अधिक लंबे के साथ प्रयोग किया जा सकता है। दो प्रतिबंध हैं: लिंक का नाम PATH_MAX से कम होना चाहिए (चेक, "/proc/self/fd/<fd>" लगभग 20 वर्ण हैं) और प्रदान किया गया आउटपुट बफर पर्याप्त बड़ा होना चाहिए। आउटपुट बफर कितना बड़ा होना चाहिए या बढ़ते बफर के साथ बार-बार readlink पर कॉल करने के लिए आप पहले lstat पर कॉल करना चाह सकते हैं।

+0

यह पहली चीज थी जिसकी मैंने कोशिश की है (यह 'मैन 2 रीडलिंक' द्वारा सुझाया गया है) और यह काम नहीं किया। मुझे बफर के आकार के बावजूद लंबे पथ के लिए 'ENAMETOOLONG' मिलता है। –

+0

@ निकिताकाकुएव: बस दोबारा जांचने के लिए: आप _did_ को वास्तविक बफर आकार को तीसरे तर्क के रूप में पास करते हैं? बेशक – MSalters

+2

। आप इसे आजमा सकते हैं और खुद के लिए देख सकते हैं। –

0

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

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

मान लीजिए कि आप अपनी वर्तमान निर्देशिका को बदलने के लिए पथ का उपयोग करते हैं, और chdir A/B/C/D/E/.../Z करें। एक बार वहां, आप नई निर्देशिका बनाते हैं और एक ही चीज करते हैं, chdir AA/AB/AC/AD/AE/.../AZ, फिर chdir BA/BB/BC/BD/... और इसी तरह ... सिस्टम में ऐसा कुछ भी नहीं है जो आपको फाइल सिस्टम में इतना गहराई से नकारने के लिए मना कर देता है (आप इसे स्वयं कोशिश कर सकते हैं, मैंने किया है और परीक्षण किया है इससे पहले) आप PATH_MAX से कहीं अधिक बड़े मानचित्र से बढ़ सकते हैं। लेकिन इसका मतलब यह है कि आप सीधे फाइल सिस्टम रूट से नहीं जा सकते हैं। आप चरणों में वहां जा सकते हैं, जितना प्रणाली की अनुमति देता है, और जहां आप रूट निर्देशिका (chroot(2) syscall के माध्यम से) या अपने वर्तमान निर्देशिका (chdir(2) syscall के माध्यम से)

शायद

आप ठीक पर निर्भर करता है काम कर inode

  • जड़ inode और curren दो स्थानीय करने वाली प्रक्रिया अवधारणाओं रहे हैं: कोई सिस्टम कॉल बनाया जा सकता है जड़ से निर्देशिका पथ काम कर अपने curren प्राप्त करने के लिए (या नहीं) सूचना है ... इस के लिए कई कारण हैं । एक ही सिस्टम में दो प्रक्रियाओं में विभिन्न कार्यशील निर्देशिकाएं हो सकती हैं, और विभिन्न रूट निर्देशिकाएं भी हो सकती हैं, इस बिंदु तक कि वे कुछ भी साझा नहीं कर पा रहे हैं और किसी की निर्देशिका से दूसरे तक पहुंचने में सक्षम नहीं हैं।
  • इनोड पथ संदिग्ध हो सकता है। खैर, यह एक निर्देशिका के लिए सच नहीं है, क्योंकि इसे दो डायरेक्ट लिंक को उसी निर्देशिका में इंगित करने की अनुमति नहीं है (यह पुरानी इकाइयों में संभव था, जहां mknod(2) सिस्टम कॉल के साथ निर्देशिकाएं बनाई जानी चाहिए, अगर आपके पास पहुंच है कुछ एचपी-ux v6 या पुरानी यूनिक्स SysV R4 आप ... प्रविष्टि के साथ निर्देशिका बना सकते हैं --- किसी निर्देशिका या समान चीजों के ग्रैनपैरेंट को इंगित करते हुए, केवल रूट होने और mknod(2) syscall का उपयोग करने के बारे में जानना) विचार यह है कि जब दो लिंक एक ही इनोड को इंगित करते हैं, जो (या दोनों) तब रूट पर जाते हैं, जो रूट इनोड से वर्तमान डीआईआर का सही मार्ग है?
  • क्रेन इनोड और रूट PATH_MAX सीमा में फिट नहीं होने के लिए पर्याप्त पथ से अलग किया जा सकता है।
  • रूट में आने में कई अलग-अलग फाइल सिस्टम (और फाइल सिस्टम प्रकार) शामिल हो सकते हैं। तो यह ऐसा कुछ नहीं है जिसे डिस्क में संग्रहित डेटा को केवल प्राप्त किया जा सके, आपको बढ़ते तालिका को जानना चाहिए।

इन कारणों से, फ़ाइल में रूट पथ जानने के लिए कर्नेल में कोई प्रत्यक्ष समर्थन नहीं है। और .. प्रविष्टि का पालन करने के लिए पथ प्राप्त करने का कोई तरीका नहीं है (और यह pwd(1) कमांड करता है) और मूल निर्देशिका में जाएं और वहां एक लिंक खोजें जो वर्तमान डीआईआर के इनोड नंबर पर हो ... और इसे तब तक दोहराएं जब तक कि पैरेंट इनोड उसी अंतिम आयोड के समान नहीं होता है।उसके बाद ही आप रूट निर्देशिका में हो जाएगा (अपने रूट निर्देशिका, कि अन्य प्रक्रियाओं जड़ निर्देशिका के सामान्य रूप में अलग है)

बस इस अभ्यास का प्रयास करें:

i=0 
while [ "$i" -lt 10000 ] 
do 
    mkdir dir-$i 
    cd dir-$i 
    i=$(expr "$i" + 1) 
done 

और देखो कितनी दूर तुम से जा सकते हैं आपके पदानुक्रम में मूल निर्देशिका।

नोट 1

एक अन्य कारण एक खुला वर्णनकर्ता से एक फ़ाइल का पथ प्राप्त करने के लिए असंभव है कि आप केवल inode (पथ आप open(2) करने के लिए इस्तेमाल करने के लिए उपयोग किया है यह वास्तविक को कोई रिश्ता नहीं हो सकता है रूट पथ, क्योंकि आप सिमलिंक का उपयोग कर सकते हैं और कार्यशील निर्देशिका के सापेक्ष उपयोग कर सकते हैं, या खुले कॉल के बीच रूट डीआईआर बदल सकते हैं और जिस समय आप पथ तक पहुंचना चाहते हैं, यह भी अस्तित्व में नहीं हो सकता है, क्योंकि आपके पास unlink(2) डी हो सकता है) इनोड जानकारी में इनोड के पथ का कोई संदर्भ नहीं है, क्योंकि फ़ाइल में एकाधिक (यहां तक ​​कि लाखों) पथ भी हो सकते हैं। इनोड में आपके पास केवल एक रेफ गिनती है, जिसका अर्थ है कि वास्तव में उस इनोड पर समाप्त होने वाले पथों की संख्या।

+0

यह एक ... दिलचस्प परिप्रेक्ष्य है। लेकिन मेरे प्रश्न के अपडेट को देखें। जैसा कि यह पता चला, मैं "PATH_MAX' की सीमा" में नहीं चल रहा था। बात यह है कि 'procfs' के लिए' रीडलिंक 'का कार्यान्वयन हमेशा एक-पेज बफर के साथ काम करता है। यदि कोई पथ इस बफर में फिट नहीं होता है, तो '-ENAMETOOLONG' वापस कर दिया जाता है। यदि 'do_proc_readlink' ने एक बड़ा बफर आवंटित किया है, तो मुझे पूरा पथ मिलेगा भले ही यह PATH_MAX से बड़ा हो। –

+0

एक सिंगल-पेज बफर के साथ आपका क्या मतलब है। सिस्टम कॉल के लिए बफर पृष्ठ सीमा पर गठबंधन करने के लिए उत्थान नहीं है, इसलिए बफर के लिए लगातार दो पृष्ठों (केवल पथ के दो बाइट्स के साथ), और यह एक चार फ़ाइल नाम और अंतिम '\ 0' चरित्र है, आप के लिए आसान है एक पथ प्राप्त कर सकते हैं जो दो सिस्टम पृष्ठों का उपयोग करता है) –

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