summaryrefslogtreecommitdiffstats
path: root/fs/umsdos/rdir.c
blob: 39605218d7b567e5cc55f718012465fa07101d8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
 *  linux/fs/umsdos/rdir.c
 *
 *  Written 1994 by Jacques Gelinas
 *
 *  Extended MS-DOS directory pure MS-DOS handling functions
 *  (For directory without EMD file).
 */

#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/limits.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>

#include <asm/uaccess.h>


extern struct inode *pseudo_root;

struct RDIR_FILLDIR {
	void *dirbuf;
	filldir_t filldir;
	int real_root;
};

static int rdir_filldir (	void *buf,
				const char *name,
				int name_len,
				off_t offset,
				ino_t ino)
{
	int ret = 0;
	struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf;

	if (d->real_root) {
		PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
		/* real root of a pseudo_rooted partition */
		if (name_len != UMSDOS_PSDROOT_LEN
		    || memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) {
			/* So it is not the /linux directory */
			if (name_len == 2 && name[0] == '.' && name[1] == '.') {
				/* Make sure the .. entry points back to the pseudo_root */
				ino = pseudo_root->i_ino;
			}
			ret = d->filldir (d->dirbuf, name, name_len, offset, ino);
		}
	} else {
		/* Any DOS directory */
		ret = d->filldir (d->dirbuf, name, name_len, offset, ino);
	}
	return ret;
}


static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
{
	struct inode *dir = filp->f_dentry->d_inode;
	struct RDIR_FILLDIR bufk;

	bufk.filldir = filldir;
	bufk.dirbuf = dirbuf;
	bufk.real_root = pseudo_root &&
			 dir->i_ino == UMSDOS_ROOT_INO && 
			 dir->i_sb == pseudo_root->i_sb;
	return fat_readdir (filp, &bufk, rdir_filldir);
}


/*
 * Lookup into a non promoted directory.
 * If the result is a directory, make sure we find out if it is
 * a promoted one or not (calling umsdos_setup_dir_inode(inode)).
 */
/* #Specification: pseudo root / DOS/..
 * In the real root directory (c:\), the directory ..
 * is the pseudo root (c:\linux).
 */
int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
{
	/* so locating "linux" will work */
	const char *name = dentry->d_name.name;
	int len = dentry->d_name.len;
	struct inode *inode;
	int ret;

	if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
	    dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
		pseudo_root->i_count++;
		d_add(dentry, pseudo_root);
		ret = 0;
		goto out;
	}

	ret = umsdos_real_lookup (dir, dentry);
	inode = dentry->d_inode;
	if ((ret == 0) && inode) {
		if (inode == pseudo_root && !nopseudo) {
			/* #Specification: pseudo root / DOS/linux
			 * Even in the real root directory (c:\), the directory
			 * /linux won't show
			 */
printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n");
			/* make the dentry negative */
			d_delete(dentry);
		}
		else if (S_ISDIR (inode->i_mode)) {
			/* We must place the proper function table
			 * depending on whether this is an MS-DOS or 
			 * a UMSDOS directory
			 */
Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
inode->i_ino));
			umsdos_setup_dir(dentry);
		}
	}
out:
	PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
	return ret;
}


int UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
{
	return umsdos_rlookup_x (dir, dentry, 0);
}


/* #Specification: dual mode / rmdir in a DOS directory
 * In a DOS (not EMD in it) directory, we use a reverse strategy
 * compared with a UMSDOS directory. We assume that a subdirectory
 * of a DOS directory is also a DOS directory. This is not always
 * true (umssync may be used anywhere), but makes sense.
 * 
 * So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
 * then we check if it is a Umsdos directory. We check if it is
 * really empty (only . .. and --linux-.--- in it). If it is true
 * we remove the EMD and do a msdos_rmdir() again.
 * 
 * In a Umsdos directory, we assume all subdirectories are also
 * Umsdos directories, so we check the EMD file first.
 */
/* #Specification: pseudo root / rmdir /DOS
 * The pseudo sub-directory /DOS can't be removed!
 * This is done even if the pseudo root is not a Umsdos
 * directory anymore (very unlikely), but an accident (under
 * MS-DOS) is always possible.
 * 
 * EPERM is returned.
 */
static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
{
	int ret, empty;

	ret = -EPERM;
	if (umsdos_is_pseudodos (dir, dentry))
		goto out;

	umsdos_lockcreate (dir);
	ret = -EBUSY;
	if (dentry->d_count > 1) {
		shrink_dcache_parent(dentry);
		if (dentry->d_count > 1)
			goto out_unlock;
	}

	ret = msdos_rmdir (dir, dentry);
	if (ret != -ENOTEMPTY)
		goto out_check;

#if 0	/* why do this? we have the dentry ... */
	ret = UMSDOS_rlookup (dir, dentry);
	PRINTK (("rrmdir lookup %d ", ret));
	if (ret)
		goto out_unlock;
	ret = -ENOTEMPTY;
#endif

	empty = umsdos_isempty (dentry);
	if (empty == 1) {
		struct dentry *temp;
		/* We have to remove the EMD file. */
		temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE, 
						UMSDOS_EMD_NAMELEN);
		ret = PTR_ERR(temp);
		if (!IS_ERR(temp)) {
			ret = 0;
			if (temp->d_inode)
				ret = msdos_unlink (dentry->d_inode, temp);
			dput(temp);
		}
		if (ret)
			goto out_unlock;
	}
	/* now retry the original ... */
	ret = msdos_rmdir (dir, dentry);

out_check:
	/* Check whether we succeeded ... */
	if (!ret)
		d_delete(dentry);

out_unlock:
	umsdos_unlockcreate (dir);
out:
	check_inode (dir);
	return ret;
}

/* #Specification: dual mode / introduction
 * One goal of UMSDOS is to allow a practical and simple coexistence
 * between MS-DOS and Linux in a single partition. Using the EMD file
 * in each directory, UMSDOS adds Unix semantics and capabilities to
 * a normal DOS filesystem. To help and simplify coexistence, here is
 * the logic related to the EMD file.
 * 
 * If it is missing, then the directory is managed by the MS-DOS driver.
 * The names are limited to DOS limits (8.3). No links, no device special
 * and pipe and so on.
 * 
 * If it is there, it is the directory. If it is there but empty, then
 * the directory looks empty. The utility umssync allows synchronisation
 * of the real DOS directory and the EMD.
 * 
 * Whenever umssync is applied to a directory without EMD, one is
 * created on the fly.  The directory is promoted to full Unix semantics.
 * Of course, the ls command will show exactly the same content as before
 * the umssync session.
 * 
 * It is believed that the user/admin will promote directories to Unix
 * semantics as needed.
 * 
 * The strategy to implement this is to use two function table (struct
 * inode_operations). One for true UMSDOS directory and one for directory
 * with missing EMD.
 * 
 * Functions related to the DOS semantic (but aware of UMSDOS) generally
 * have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
 * from the one with full UMSDOS semantics.
 */
static struct file_operations umsdos_rdir_operations =
{
	NULL,			/* lseek - default */
	dummy_dir_read,		/* read */
	NULL,			/* write - bad */
	UMSDOS_rreaddir,	/* readdir */
	NULL,			/* poll - default */
	UMSDOS_ioctl_dir,	/* ioctl - default */
	NULL,			/* mmap */
	NULL,			/* no special open code */
	NULL,			/* flush */
	NULL,			/* no special release code */
	NULL			/* fsync */
};

struct inode_operations umsdos_rdir_inode_operations =
{
	&umsdos_rdir_operations,	/* default directory file-ops */
	msdos_create,		/* create */
	UMSDOS_rlookup,		/* lookup */
	NULL,			/* link */
	msdos_unlink,		/* unlink */
	NULL,			/* symlink */
	msdos_mkdir,		/* mkdir */
	UMSDOS_rrmdir,		/* rmdir */
	NULL,			/* mknod */
	msdos_rename,		/* rename */
	NULL,			/* readlink */
	NULL,			/* followlink */
	NULL,			/* readpage */
	NULL,			/* writepage */
	NULL,			/* bmap */
	NULL,			/* truncate */
	NULL,			/* permission */
	NULL,			/* smap */
	NULL,			/* updatepage */
	NULL,			/* revalidate */
};