forked from Minki/linux
9769f4eb3f
The current isofs treatment of hidden files is flawed in two ways. First, it does not provide sufficient granularity; it hides both 'hidden' files and 'associated' files (resource fork for Mac files). Second, the default behavior to completely strip hidden files, while an admirable implementation of the spec, is a poor choice given the real world use of hidden files as a poor mans copy protection scheme for MSDOS and Windows based systems. A longer description of this is available here: http://www.uwsg.iu.edu/hypermail/linux/kernel/0205.3/0267.html This patch was originally built after a few private conversations with Alan Cox; I shamefully failed to persist in seeing it go forward, I hope to make amends now. This patch introduces granularity by allowing explicit control for both hidden and associated files. It also reverses the default so that by default, hidden files are treated as regular files on the iso9660 file system. This allow Wine to process Windows CDs, including those that are hybrid Mac/Windows CDs properly and completely, without our having to go muck up peoples fstabs as we do now. (I have tested this with such a hybrid + hidden CD and have verified that this patch works as claimed). Signed-off-by: Jeremy White <jwhite@codeweavers.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
193 lines
4.2 KiB
C
193 lines
4.2 KiB
C
/*
|
|
* linux/fs/isofs/namei.c
|
|
*
|
|
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
|
|
*
|
|
* (C) 1991 Linus Torvalds - minix filesystem
|
|
*/
|
|
|
|
#include <linux/config.h> /* Joliet? */
|
|
#include <linux/smp_lock.h>
|
|
#include "isofs.h"
|
|
|
|
/*
|
|
* ok, we cannot use strncmp, as the name is not in our data space.
|
|
* Thus we'll have to use isofs_match. No big problem. Match also makes
|
|
* some sanity tests.
|
|
*/
|
|
static int
|
|
isofs_cmp(struct dentry * dentry, const char * compare, int dlen)
|
|
{
|
|
struct qstr qstr;
|
|
|
|
if (!compare)
|
|
return 1;
|
|
|
|
/* check special "." and ".." files */
|
|
if (dlen == 1) {
|
|
/* "." */
|
|
if (compare[0] == 0) {
|
|
if (!dentry->d_name.len)
|
|
return 0;
|
|
compare = ".";
|
|
} else if (compare[0] == 1) {
|
|
compare = "..";
|
|
dlen = 2;
|
|
}
|
|
}
|
|
|
|
qstr.name = compare;
|
|
qstr.len = dlen;
|
|
return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
|
|
}
|
|
|
|
/*
|
|
* isofs_find_entry()
|
|
*
|
|
* finds an entry in the specified directory with the wanted name. It
|
|
* returns the inode number of the found entry, or 0 on error.
|
|
*/
|
|
static unsigned long
|
|
isofs_find_entry(struct inode *dir, struct dentry *dentry,
|
|
unsigned long *block_rv, unsigned long* offset_rv,
|
|
char * tmpname, struct iso_directory_record * tmpde)
|
|
{
|
|
unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
|
|
unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
|
|
unsigned long block, f_pos, offset, block_saved, offset_saved;
|
|
struct buffer_head * bh = NULL;
|
|
struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
|
|
|
|
if (!ISOFS_I(dir)->i_first_extent)
|
|
return 0;
|
|
|
|
f_pos = 0;
|
|
offset = 0;
|
|
block = 0;
|
|
|
|
while (f_pos < dir->i_size) {
|
|
struct iso_directory_record * de;
|
|
int de_len, match, i, dlen;
|
|
char *dpnt;
|
|
|
|
if (!bh) {
|
|
bh = isofs_bread(dir, block);
|
|
if (!bh)
|
|
return 0;
|
|
}
|
|
|
|
de = (struct iso_directory_record *) (bh->b_data + offset);
|
|
|
|
de_len = *(unsigned char *) de;
|
|
if (!de_len) {
|
|
brelse(bh);
|
|
bh = NULL;
|
|
f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
|
|
block = f_pos >> bufbits;
|
|
offset = 0;
|
|
continue;
|
|
}
|
|
|
|
block_saved = bh->b_blocknr;
|
|
offset_saved = offset;
|
|
offset += de_len;
|
|
f_pos += de_len;
|
|
|
|
/* Make sure we have a full directory entry */
|
|
if (offset >= bufsize) {
|
|
int slop = bufsize - offset + de_len;
|
|
memcpy(tmpde, de, slop);
|
|
offset &= bufsize - 1;
|
|
block++;
|
|
brelse(bh);
|
|
bh = NULL;
|
|
if (offset) {
|
|
bh = isofs_bread(dir, block);
|
|
if (!bh)
|
|
return 0;
|
|
memcpy((void *) tmpde + slop, bh->b_data, offset);
|
|
}
|
|
de = tmpde;
|
|
}
|
|
|
|
dlen = de->name_len[0];
|
|
dpnt = de->name;
|
|
|
|
if (sbi->s_rock &&
|
|
((i = get_rock_ridge_filename(de, tmpname, dir)))) {
|
|
dlen = i; /* possibly -1 */
|
|
dpnt = tmpname;
|
|
#ifdef CONFIG_JOLIET
|
|
} else if (sbi->s_joliet_level) {
|
|
dlen = get_joliet_filename(de, tmpname, dir);
|
|
dpnt = tmpname;
|
|
#endif
|
|
} else if (sbi->s_mapping == 'a') {
|
|
dlen = get_acorn_filename(de, tmpname, dir);
|
|
dpnt = tmpname;
|
|
} else if (sbi->s_mapping == 'n') {
|
|
dlen = isofs_name_translate(de, tmpname, dir);
|
|
dpnt = tmpname;
|
|
}
|
|
|
|
/*
|
|
* Skip hidden or associated files unless hide or showassoc,
|
|
* respectively, is set
|
|
*/
|
|
match = 0;
|
|
if (dlen > 0 &&
|
|
(sbi->s_hide =='n' ||
|
|
(!(de->flags[-sbi->s_high_sierra] & 1))) &&
|
|
(sbi->s_showassoc =='y' ||
|
|
(!(de->flags[-sbi->s_high_sierra] & 4)))) {
|
|
match = (isofs_cmp(dentry, dpnt, dlen) == 0);
|
|
}
|
|
if (match) {
|
|
isofs_normalize_block_and_offset(de,
|
|
&block_saved,
|
|
&offset_saved);
|
|
*block_rv = block_saved;
|
|
*offset_rv = offset_saved;
|
|
brelse(bh);
|
|
return 1;
|
|
}
|
|
}
|
|
brelse(bh);
|
|
return 0;
|
|
}
|
|
|
|
struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
|
|
{
|
|
int found;
|
|
unsigned long block, offset;
|
|
struct inode *inode;
|
|
struct page *page;
|
|
|
|
dentry->d_op = dir->i_sb->s_root->d_op;
|
|
|
|
page = alloc_page(GFP_USER);
|
|
if (!page)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
lock_kernel();
|
|
found = isofs_find_entry(dir, dentry,
|
|
&block, &offset,
|
|
page_address(page),
|
|
1024 + page_address(page));
|
|
__free_page(page);
|
|
|
|
inode = NULL;
|
|
if (found) {
|
|
inode = isofs_iget(dir->i_sb, block, offset);
|
|
if (!inode) {
|
|
unlock_kernel();
|
|
return ERR_PTR(-EACCES);
|
|
}
|
|
}
|
|
unlock_kernel();
|
|
if (inode)
|
|
return d_splice_alias(inode, dentry);
|
|
d_add(dentry, inode);
|
|
return NULL;
|
|
}
|