سؤال
بالنظر إلى مسار إلى ملف أو دليل ، كيف يمكنني تحديد نقطة التثبيت لهذا الملف؟ على سبيل المثال ، إذا /tmp
تم تركيبه على أنه أ tmpfs
نظام الملفات ثم أعطى اسم الملف /tmp/foo/bar
أريد أن أعرف أنه تم تخزينه على tmpfs
متجذر في /tmp
.
سيكون هذا في C ++ وأرغب في تجنب استدعاء الأوامر الخارجية عبر system()
. يجب أن يكون القانون قويًا-ليس بالضرورة ضد العبث المتعمد ولكن بالتأكيد في مواجهة نقاط التثبيت المتداخلة ، والروابط ، إلخ.
لم أتمكن من العثور على مكالمة نظام بسيطة للقيام بذلك. يبدو أنني سأضطر إلى كتابة الشيك بنفسي. إليك مخططًا تقريبيًا لما أخطط له.
- قم بتعيين اسم الملف A la the
readlink
أمر شل. كيف؟ - اقرأ
/etc/mtab
معgetmntent()
& co. - تحديد إدخال جبل المقابل للملف. كيف؟
بالنسبة إلى #1 ، هل هناك مكالمة نظام بسيطة أو أحتاج إلى قراءة كل مكون من مكونات الدليل وحلها readlink(2)
إذا كانت تصفح؟ والتعامل مع .
و ..
نفسي؟ يبدو وكأنه ألم.
بالنسبة لـ #3 ، لدي أفكار مختلفة حول كيفية القيام بذلك. لست متأكدا ما هو الأفضل.
open()
الملف ، والديه ، والد الوالد ، وما إلى ذلك باستخدامopenat(fd, "..")
حتى أصل إلى واحد من/etc/mtab
إدخالات. ((كيف أعرف متى أفعل؟fstat()
لهم ومقارنة أرقام inode؟)- ابحث عن أطول اسم دليل في جدول Mount ، وهو عبارة عن سلسلة فرعية لاسم ملفي.
أنا أميل نحو الخيار الأول ، لكن قبل أن أقوم بترميز كل شيء ، أريد أن أتأكد من أنني لا أطل على أي شيء-وظيفة مدمجة حقًا تقوم بذلك بالفعل!
المحلول
هذا ما توصلت إليه. اتضح أنه عادة ما لا توجد حاجة للتكرار من خلال الدلائل الأم. كل ما عليك فعله هو الحصول على رقم جهاز الملف ثم ابحث عن إدخال Mount المقابل مع نفس رقم الجهاز.
struct mntent *mountpoint(char *filename, struct mntent *mnt, char *buf, size_t buflen)
{
struct stat s;
FILE * fp;
dev_t dev;
if (stat(filename, &s) != 0) {
return NULL;
}
dev = s.st_dev;
if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
return NULL;
}
while (getmntent_r(fp, mnt, buf, buflen)) {
if (stat(mnt->mnt_dir, &s) != 0) {
continue;
}
if (s.st_dev == dev) {
endmntent(fp);
return mnt;
}
}
endmntent(fp);
// Should never reach here.
errno = EINVAL;
return NULL;
}
بفضل RichardPennington للرؤوس على realpath()
, وعند مقارنة أرقام الجهاز بدلاً من أرقام inode.
نصائح أخرى
يمكنك أن تبدأ بـ RealPath وتعمل في طريقك للتحقق من كل دليل مع Stat لمعرفة ما إذا كان على نفس الجهاز. يبدو أنه يجب أن يكون هناك طريقة أبسط.
يحرر:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *p;
char path[PATH_MAX];
struct stat buf;
dev_t dev;
if (realpath(argv[1], path) == NULL) {
fprintf(stderr, "can't find %s\n", argv[1]);
exit(1);
}
if (stat(path, &buf) != 0) {
fprintf(stderr, "can't statind %s\n", path);
exit(1);
}
dev = buf.st_dev;
while((p = strrchr(path, '/'))) {
*p = '\0';
stat(path, &buf);
if (buf.st_dev != dev) {
printf("mount point = %s\n", path);
exit(0);
}
}
printf("mount point = /\n");
}
شكرًا على الهاء ؛-)
هذا عمل بالنسبة لي على OSX ، والذي لا يوفر وظائف mntent. في C ++:
struct stat fileStat;
int result = stat(path, &fileStat);
if (result != 0) {
// handle error
}
struct statfs* mounts;
int numMounts = getmntinfo(&mounts, MNT_WAIT);
if (numMounts == 0) {
// handle error
}
for (int i = 0; i < numMounts; i++) {
if (fileStat.st_dev == mounts[i].f_fsid.val[0])
// mounts[i].f_mntonname is the mount path
}
يجب أن تكون قادرًا على القراءة /etc/mtab
, ، قم بتحليل جميع المواقع التي يتم فيها تثبيت شيء ما بالفعل ، ومعرفة ما إذا كان أي من ملفاتك المعنية موجودة في أي من هذه المواقع (أو المخرجين الفرعيين). يجب ألا تحتاج إلى أي وظائف نظام خاصة لهذا ، إذا كان لديك نقاط التثبيت ومسارات الملف كأسلاك ، يمكنك معالجتها باستخدام وظائف معالجة السلسلة العادية.
من الواضح أن Symlinks يمكن أن ترمي وجع في هذه العملية برمتها. يجب تحويل أي مسار ملف يتضمن Symlink إلى مسار "فعلي" قبل معالجته. نأمل أن تكون هناك طريقة للقيام بذلك دون التحقق بشكل فردي من ملف وكل من والديه ، ولكن يمكنك دائمًا أن تحظى به إذا كان عليك ذلك. ربما تريد استخدامها realpath
لإزالة symlinks.