Linux-2.4.0-test2
[davej-history.git] / fs / umsdos / ioctl.c
bloba8adf6ed87778fbcb29cdd6c75e93a7fe87b59e2
1 /*
2 * linux/fs/umsdos/ioctl.c
4 * Written 1993 by Jacques Gelinas
6 * Extended MS-DOS ioctl directory handling functions
7 */
9 #include <asm/uaccess.h>
10 #include <linux/errno.h>
11 #include <linux/mm.h>
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/fs.h>
15 #include <linux/msdos_fs.h>
16 #include <linux/umsdos_fs.h>
18 struct UMSDOS_DIR_ONCE {
19 struct dirent *ent;
20 int count;
24 * Record a single entry the first call.
25 * Return -EINVAL the next one.
27 static int umsdos_ioctl_fill (
28 void *buf,
29 const char *name,
30 int name_len,
31 off_t offset,
32 ino_t ino)
34 int ret = -EINVAL;
35 struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
37 if (d->count == 0) {
38 copy_to_user (d->ent->d_name, name, name_len);
39 put_user ('\0', d->ent->d_name + name_len);
40 put_user (name_len, &d->ent->d_reclen);
41 put_user (ino, &d->ent->d_ino);
42 put_user (offset, &d->ent->d_off);
43 d->count = 1;
44 ret = 0;
46 return ret;
51 * Perform special function on a directory
53 /* #Specification: ioctl / prototypes
54 * The official prototype for the umsdos ioctl on directory
55 * is:
57 * int ioctl (
58 * int fd, // File handle of the directory
59 * int cmd, // command
60 * struct umsdos_ioctl *data)
62 * The struct and the commands are defined in linux/umsdos_fs.h.
64 * umsdos_progs/umsdosio.c provide an interface in C++ to all
65 * these ioctl. umsdos_progs/udosctl is a small utility showing
66 * all this.
68 * These ioctl generally allow one to work on the EMD or the
69 * DOS directory independently. These are essential to implement
70 * the synchronise.
72 int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
73 unsigned long data_ptr)
75 struct dentry *dentry = filp->f_dentry;
76 struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr;
77 int ret;
78 struct file new_filp;
79 struct umsdos_ioctl data;
81 Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
82 dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
84 /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
85 if (cmd != UMSDOS_GETVERSION
86 && cmd != UMSDOS_READDIR_DOS
87 && cmd != UMSDOS_READDIR_EMD
88 && cmd != UMSDOS_INIT_EMD
89 && cmd != UMSDOS_CREAT_EMD
90 && cmd != UMSDOS_RENAME_DOS
91 && cmd != UMSDOS_UNLINK_EMD
92 && cmd != UMSDOS_UNLINK_DOS
93 && cmd != UMSDOS_RMDIR_DOS
94 && cmd != UMSDOS_STAT_DOS
95 && cmd != UMSDOS_DOS_SETUP)
96 return fat_dir_ioctl (dir, filp, cmd, data_ptr);
98 /* #Specification: ioctl / acces
99 * Only root (effective id) is allowed to do IOCTL on directory
100 * in UMSDOS. EPERM is returned for other user.
103 * Well, not all cases require write access, but it simplifies
104 * the code, and let's face it, there is only one client (umssync)
105 * for all this.
107 ret = verify_area (VERIFY_WRITE, (void *) data_ptr,
108 sizeof (struct umsdos_ioctl));
109 if (ret < 0)
110 goto out;
112 ret = -EPERM;
113 if (current->euid != 0 && cmd != UMSDOS_GETVERSION)
114 goto out;
116 ret = -EINVAL;
117 if (cmd == UMSDOS_GETVERSION) {
118 /* #Specification: ioctl / UMSDOS_GETVERSION
119 * The field version and release of the structure
120 * umsdos_ioctl are filled with the version and release
121 * number of the fs code in the kernel. This will allow
122 * some form of checking. Users won't be able to run
123 * incompatible utility such as the synchroniser (umssync).
124 * umsdos_progs/umsdosio.c enforce this checking.
126 * Return always 0.
128 put_user (UMSDOS_VERSION, &idata->version);
129 put_user (UMSDOS_RELEASE, &idata->release);
130 ret = 0;
131 goto out;
133 if (cmd == UMSDOS_READDIR_DOS) {
134 /* #Specification: ioctl / UMSDOS_READDIR_DOS
135 * One entry is read from the DOS directory at the current
136 * file position. The entry is put as is in the dos_dirent
137 * field of struct umsdos_ioctl.
139 * Return > 0 if success.
141 struct UMSDOS_DIR_ONCE bufk;
143 bufk.count = 0;
144 bufk.ent = &idata->dos_dirent;
146 fat_readdir (filp, &bufk, umsdos_ioctl_fill);
148 ret = bufk.count == 1 ? 1 : 0;
149 goto out;
151 if (cmd == UMSDOS_READDIR_EMD) {
152 /* #Specification: ioctl / UMSDOS_READDIR_EMD
153 * One entry is read from the EMD at the current
154 * file position. The entry is put as is in the umsdos_dirent
155 * field of struct umsdos_ioctl. The corresponding mangled
156 * DOS entry name is put in the dos_dirent field.
158 * All entries are read including hidden links. Blank
159 * entries are skipped.
161 * Return > 0 if success.
163 struct dentry *demd;
165 /* The absence of the EMD is simply seen as an EOF */
166 demd = umsdos_get_emd_dentry(dentry);
167 ret = PTR_ERR(demd);
168 if (IS_ERR(demd))
169 goto out;
170 ret = 0;
171 if (!demd->d_inode)
172 goto read_dput;
174 fill_new_filp(&new_filp, demd);
175 new_filp.f_pos = filp->f_pos;
176 while (new_filp.f_pos < demd->d_inode->i_size) {
177 off_t f_pos = new_filp.f_pos;
178 struct umsdos_dirent entry;
179 struct umsdos_info info;
181 ret = umsdos_emd_dir_readentry (&new_filp, &entry);
182 if (ret)
183 break;
184 if (entry.name_len <= 0)
185 continue;
187 umsdos_parse (entry.name, entry.name_len, &info);
188 info.f_pos = f_pos;
189 umsdos_manglename (&info);
190 ret = -EFAULT;
191 if (copy_to_user (&idata->umsdos_dirent, &entry,
192 sizeof (entry)))
193 break;
194 if (copy_to_user (&idata->dos_dirent.d_name,
195 info.fake.fname,
196 info.fake.len + 1))
197 break;
198 ret = entry.name_len;
199 break;
201 /* update the original f_pos */
202 filp->f_pos = new_filp.f_pos;
203 read_dput:
204 d_drop(demd);
205 dput(demd);
206 goto out;
208 if (cmd == UMSDOS_INIT_EMD) {
209 /* #Specification: ioctl / UMSDOS_INIT_EMD
210 * The UMSDOS_INIT_EMD command makes sure the EMD
211 * exists for a directory. If it does not, it is
212 * created. Also, it makes sure the directory function
213 * table (struct inode_operations) is set to the UMSDOS
214 * semantic. This mean that umssync may be applied to
215 * an "opened" msdos directory, and it will change behavior
216 * on the fly.
218 * Return 0 if success.
221 ret = umsdos_make_emd(dentry);
222 Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
223 dentry->d_parent->d_name.name, dentry->d_name.name, ret));
224 umsdos_setup_dir (dentry);
225 goto out;
228 ret = -EFAULT;
229 if (copy_from_user (&data, idata, sizeof (data)))
230 goto out;
232 if (cmd == UMSDOS_CREAT_EMD) {
233 /* #Specification: ioctl / UMSDOS_CREAT_EMD
234 * The umsdos_dirent field of the struct umsdos_ioctl is used
235 * as is to create a new entry in the EMD of the directory.
236 * The DOS directory is not modified.
237 * No validation is done (yet).
239 * Return 0 if success.
241 struct umsdos_info info;
243 /* This makes sure info.entry and info in general
244 * is correctly initialised
246 memcpy (&info.entry, &data.umsdos_dirent,
247 sizeof (data.umsdos_dirent));
248 umsdos_parse (data.umsdos_dirent.name
249 ,data.umsdos_dirent.name_len, &info);
250 ret = umsdos_newentry (dentry, &info);
251 goto out;
253 else if (cmd == UMSDOS_RENAME_DOS) {
254 struct dentry *old_dentry, *new_dentry; /* FIXME */
256 /* #Specification: ioctl / UMSDOS_RENAME_DOS
257 * A file or directory is renamed in a DOS directory
258 * (not moved across directory). The source name
259 * is in the dos_dirent.name field and the destination
260 * is in umsdos_dirent.name field.
262 * This ioctl allows umssync to rename a mangled file
263 * name before syncing it back in the EMD.
265 old_dentry = umsdos_lookup_dentry (dentry,
266 data.dos_dirent.d_name,
267 data.dos_dirent.d_reclen ,1);
268 ret = PTR_ERR(old_dentry);
269 if (IS_ERR(old_dentry))
270 goto out;
271 new_dentry = umsdos_lookup_dentry (dentry,
272 data.umsdos_dirent.name,
273 data.umsdos_dirent.name_len, 1);
274 ret = PTR_ERR(new_dentry);
275 if (!IS_ERR(new_dentry)) {
276 printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
277 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
278 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
279 ret = msdos_rename (dir, old_dentry, dir, new_dentry);
280 d_drop(new_dentry);
281 d_drop(old_dentry);
282 dput(new_dentry);
284 dput(old_dentry);
285 goto out;
287 else if (cmd == UMSDOS_UNLINK_EMD) {
288 /* #Specification: ioctl / UMSDOS_UNLINK_EMD
289 * The umsdos_dirent field of the struct umsdos_ioctl is used
290 * as is to remove an entry from the EMD of the directory.
291 * No validation is done (yet). The mode field is used
292 * to validate S_ISDIR or S_ISREG.
294 * Return 0 if success.
296 struct umsdos_info info;
298 /* This makes sure info.entry and info in general
299 * is correctly initialised
301 memcpy (&info.entry, &data.umsdos_dirent,
302 sizeof (data.umsdos_dirent));
303 umsdos_parse (data.umsdos_dirent.name,
304 data.umsdos_dirent.name_len, &info);
305 ret = umsdos_delentry (dentry, &info,
306 S_ISDIR (data.umsdos_dirent.mode));
307 if (ret) {
308 printk(KERN_WARNING
309 "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
310 dentry->d_name.name, info.entry.name, ret);
312 goto out;
314 else if (cmd == UMSDOS_UNLINK_DOS) {
315 struct dentry *temp;
317 /* #Specification: ioctl / UMSDOS_UNLINK_DOS
318 * The dos_dirent field of the struct umsdos_ioctl is used to
319 * execute a msdos_unlink operation. The d_name and d_reclen
320 * fields are used.
322 * Return 0 if success.
324 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
325 data.dos_dirent.d_reclen, 1);
326 ret = PTR_ERR(temp);
327 if (IS_ERR(temp))
328 goto out;
329 ret = -ENOENT;
330 if (temp->d_inode) {
331 ret = -EISDIR;
332 if (!S_ISDIR(temp->d_inode->i_mode))
333 ret = msdos_unlink (dir, temp);
334 if (!ret)
335 d_delete(temp);
337 dput (temp);
338 goto out;
340 else if (cmd == UMSDOS_RMDIR_DOS) {
341 struct dentry *temp;
343 /* #Specification: ioctl / UMSDOS_RMDIR_DOS
344 * The dos_dirent field of the struct umsdos_ioctl is used to
345 * execute a msdos_rmdir operation. The d_name and d_reclen
346 * fields are used.
348 * Return 0 if success.
350 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
351 data.dos_dirent.d_reclen, 1);
352 ret = PTR_ERR(temp);
353 if (IS_ERR(temp))
354 goto out;
355 ret = -ENOENT;
356 if (temp->d_inode) {
357 ret = -ENOTDIR;
358 if (S_ISDIR(temp->d_inode->i_mode))
359 ret = msdos_rmdir (dir, temp);
360 if (!ret)
361 d_delete(temp);
363 dput (temp);
364 goto out;
366 } else if (cmd == UMSDOS_STAT_DOS) {
367 /* #Specification: ioctl / UMSDOS_STAT_DOS
368 * The dos_dirent field of the struct umsdos_ioctl is
369 * used to execute a stat operation in the DOS directory.
370 * The d_name and d_reclen fields are used.
372 * The following field of umsdos_ioctl.stat are filled.
374 * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
375 * Return 0 if success.
377 struct dentry *dret;
378 struct inode *inode;
380 dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
381 data.dos_dirent.d_reclen, 1);
382 ret = PTR_ERR(dret);
383 if (IS_ERR(dret))
384 goto out;
385 ret = -ENOENT;
386 inode = dret->d_inode;
387 if (inode) {
388 data.stat.st_ino = inode->i_ino;
389 data.stat.st_mode = inode->i_mode;
390 data.stat.st_size = inode->i_size;
391 data.stat.st_atime = inode->i_atime;
392 data.stat.st_ctime = inode->i_ctime;
393 data.stat.st_mtime = inode->i_mtime;
394 ret = -EFAULT;
395 if (!copy_to_user (&idata->stat, &data.stat,
396 sizeof (data.stat)))
397 ret = 0;
399 dput(dret);
400 goto out;
402 else if (cmd == UMSDOS_DOS_SETUP) {
403 /* #Specification: ioctl / UMSDOS_DOS_SETUP
404 * The UMSDOS_DOS_SETUP ioctl allow changing the
405 * default permission of the MS-DOS filesystem driver
406 * on the fly. The MS-DOS driver applies global permissions
407 * to every file and directory. Normally these permissions
408 * are controlled by a mount option. This is not
409 * available for root partition, so a special utility
410 * (umssetup) is provided to do this, normally in
411 * /etc/rc.local.
413 * Be aware that this applies ONLY to MS-DOS directories
414 * (those without EMD --linux-.---). Umsdos directory
415 * have independent (standard) permission for each
416 * and every file.
418 * The field umsdos_dirent provide the information needed.
419 * umsdos_dirent.uid and gid sets the owner and group.
420 * umsdos_dirent.mode set the permissions flags.
422 dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
423 dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
424 dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
425 ret = 0;
427 out:
428 Printk (("ioctl %d, returning %d\n", cmd, ret));
429 return ret;