快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Cygwin checkout-2008-09-28
vs2008
欢迎转载,但请保留作者信息
在cygwin初始化的时候,会查询根目录下是否有/etc/fstab文件,如果这个文件存在,cygwin将读取它,从中取得windows路径和posix路径之间的映射关系。
本文尝试对此做一点分析。
cygwin初始化的时候,将调用一个叫mount_info::init的函数,其调用栈如下:
> cygwin.dll!mount_info::init() 行116 + 0x15 字节 C++
cygwin.dll!user_shared_initialize() 行227 C++
cygwin.dll!dll_crt0_1(void * __formal=0x00000000) 行898 C++
cygwin.dll!_cygtls::call2(unsigned long (void *, void *)* func=0x10012000, void * arg=0x00000000, void * buf=0x00216fb8) 行72 + 0xd 字节 C++
cygwin.dll!_cygtls::call(unsigned long (void *, void *)* func=0x10012000, void * arg=0x00000000) 行66 C++
cygwin.dll!_dll_crt0() 行1082 + 0xc 字节 C++
cygwin.dll!cygwin_dll_init() 行1121 C++
bash.exe!main(int argc=0x00000003, char * * argv=0x00c56748) 行769 + 0x8 字节 C
看看这个函数做了什么事情:
void
mount_info::init ()
{
nmounts = 0;
PWCHAR pathend;
WCHAR path[PATH_MAX];
pathend = wcpcpy (path, cygwin_shared->installation_root);
create_root_entry (path);
pathend = wcpcpy (pathend, L"\\etc\\fstab");
if (from_fstab (false, path, pathend) /* The single | is correct! */
| from_fstab (true, path, pathend))
return;
/* FIXME: Remove warning message before releasing 1.7.0. */
small_printf ("Huh? No /etc/fstab file in %W? Using default root and cygdrive prefix...\n", path);
}
在这里cygwin_shared->installation_root取值为正在运行的exe文件所在目录的上一级目录,比如exe文件为
"\??\f:\embed\etools\Debug\bin\bash.exe"
则cygwin_shared->installation_root的值就是
"\??\f:\embed\etools\Debug\"
这段代码将这个路径传入from_fstab函数,继续往下看:
bool
mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
{
UNICODE_STRING upath;
OBJECT_ATTRIBUTES attr;
CYG_IO_STATUS_BLOCK io;
NTSTATUS status;
HANDLE fh;
…………….
RtlInitUnicodeString (&upath, fstab);
InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
debug_printf ("Try to read mounts from %W", fstab);
status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS (status))
{
debug_printf ("NtOpenFile(%S) failed, %p", &upath, status);
return false;
}
char buf[NT_MAX_PATH];
char *got = buf;
DWORD len = 0;
unsigned line = 1;
/* Using buffer size - 2 leaves space to append two \0. */
while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, got,
(sizeof (buf) - 2) - (got - buf), NULL, NULL)))
{
char *end;
len = io.Information;
/* Set end marker. */
got[len] = got[len + 1] = '\0';
/* Set len to the absolute len of bytes in buf. */
len += got - buf;
/* Reset got to start reading at the start of the buffer again. */
got = buf;
retry:
bool got_nl = false;
while (got < buf + len && (end = strchr (got, '\n')))
{
got_nl = true;
end[end[-1] == '\r' ? -1 : 0] = '\0';
if (!from_fstab_line (got, user))
goto done;
got = end + 1;
++line;
}
if (len < (sizeof (buf) - 2))
break;
/* Check if the buffer contained at least one \n. If not, the
line length is > 32K. We don't take such long lines. Print
a debug message and skip this line entirely. */
if (!got_nl)
{
system_printf ("%W: Line %d too long, skipping...", fstab, line);
while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, buf,
(sizeof (buf) - 2), NULL, NULL)))
{
len = io.Information;
buf[len] = buf[len + 1] = '\0';
got = strchr (buf, '\n');
if (got)
{
++got;
++line;
goto retry;
}
}
got = buf;
break;
}
/* We have to cyg_read once more. Move remaining bytes to the start of
the buffer and reposition got so that it points to the end of
the remaining bytes. */
len = buf + len - got;
memmove (buf, got, len);
got = buf + len;
buf[len] = buf[len + 1] = '\0';
}
/* Catch a last line without trailing \n. */
if (got > buf)
from_fstab_line (got, user);
done:
NtClose (fh);
return true;
}
很显然,这一段代码将从fstab文件中一行一行地取出数据,并将此行字符串的指针传递给from_fstab_line函数,对每行数据的分析由from_fstab_line函数完成。
由于cygwin对每一行数据的分析都是通过from_fstab_line函数完成的,因此我们直接看看它做了什么:
bool
mount_info::from_fstab_line (char *line, bool user)
{
char *native_path, *posix_path, *fs_type;
/* First field: Native path. */
char *c = skip_ws (line);
if (!*c || *c == '#')
return true;
char *cend = find_ws (c);
*cend = '\0';
native_path = conv_fstab_spaces (c);
/* Second field: POSIX path. */
c = skip_ws (cend + 1);
if (!*c)
return true;
cend = find_ws (c);
*cend = '\0';
posix_path = conv_fstab_spaces (c);
/* Third field: FS type. */
c = skip_ws (cend + 1);
if (!*c)
return true;
cend = find_ws (c);
*cend = '\0';
fs_type = c;
/* Forth field: Flags. */
c = skip_ws (cend + 1);
if (!*c)
return true;
cend = find_ws (c);
*cend = '\0';
unsigned mount_flags = MOUNT_SYSTEM | MOUNT_BINARY;
if (!read_flags (c, mount_flags))
return true;
if (user)
mount_flags &= ~MOUNT_SYSTEM;
if (!strcmp (fs_type, "cygdrive"))
{
cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
slashify (posix_path, cygdrive, 1);
cygdrive_len = strlen (cygdrive);
}
else
{
int res = mount_table->add_item (native_path, posix_path, mount_flags);
if (res && get_errno () == EMFILE)
return false;
}
return true;
}
从这个函数可以很清楚地看出每一行数据的意义:
如果这个行以#号开关,那么它是一个注释。否则就认为这是一行有意义的数据。
每一行数据有4个字段,每个字段间以’\t’做为分隔符号。
第一个字段为windows本地路径,比如c:\windows之类的。
第二个字段为对应的posix路径名称,比如/windows。
第三个字段为文件系统类型,但是从这段代码可以看出,只有当它为“cygdrive”时有特殊意义,否则丢弃这个段的内容。
最后一个是特殊标志,它的允许取值来自于下面的定义:
struct opt
{
const char *name;
unsigned val;
bool clear;
} oopts[] =
{
{"user", MOUNT_SYSTEM, 1},
{"nouser", MOUNT_SYSTEM, 0},
{"binary", MOUNT_BINARY, 0},
{"text", MOUNT_BINARY, 1},
{"exec", MOUNT_EXEC, 0},
{"notexec", MOUNT_NOTEXEC, 0},
{"cygexec", MOUNT_CYGWIN_EXEC, 0},
{"nosuid", 0, 0},
{"acl", MOUNT_NOACL, 1},
{"noacl", MOUNT_NOACL, 0},
{"posix=1", MOUNT_NOPOSIX, 1},
{"posix=0", MOUNT_NOPOSIX, 0}
};
它只能取上面列出的字符串组合。
比如我们定义下面一行数据:
c:\windows /windows/ xpdir user
这样定义之后,我们就可以在bash下面使用
cd /windows
命令了,它将当前路径改为c:\windows。
需要注意的是:在fstab没有数据的情况下,cygwin仍然允许通过/cygdrive/c这样的路径访问c盘的内容,其它的盘符同理。
cygwin关键技术:fork(2009-9-4)
cygwin关键技术:设备模拟(2009-9-4)
cygwin关键技术:cygheap(2009-9-2)
cygwin关键技术:tls(2009-8-24)