Linux-2.3.3 and a short hiatus..
[davej-history.git] / fs / umsdos / ioctl.c
blobf26d19ba8bc4194fbc077b03b2d2aef1f8725f2f
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.
220 extern struct inode_operations umsdos_rdir_inode_operations;
222 ret = umsdos_make_emd(dentry);
223 Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
224 dentry->d_parent->d_name.name, dentry->d_name.name, ret));
225 dir->i_op = (ret == 0)
226 ? &umsdos_dir_inode_operations
227 : &umsdos_rdir_inode_operations;
228 goto out;
231 ret = -EFAULT;
232 if (copy_from_user (&data, idata, sizeof (data)))
233 goto out;
235 if (cmd == UMSDOS_CREAT_EMD) {
236 /* #Specification: ioctl / UMSDOS_CREAT_EMD
237 * The umsdos_dirent field of the struct umsdos_ioctl is used
238 * as is to create a new entry in the EMD of the directory.
239 * The DOS directory is not modified.
240 * No validation is done (yet).
242 * Return 0 if success.
244 struct umsdos_info info;
246 /* This makes sure info.entry and info in general
247 * is correctly initialised
249 memcpy (&info.entry, &data.umsdos_dirent,
250 sizeof (data.umsdos_dirent));
251 umsdos_parse (data.umsdos_dirent.name
252 ,data.umsdos_dirent.name_len, &info);
253 ret = umsdos_newentry (dentry, &info);
254 goto out;
256 else if (cmd == UMSDOS_RENAME_DOS) {
257 struct dentry *old_dentry, *new_dentry; /* FIXME */
259 /* #Specification: ioctl / UMSDOS_RENAME_DOS
260 * A file or directory is renamed in a DOS directory
261 * (not moved across directory). The source name
262 * is in the dos_dirent.name field and the destination
263 * is in umsdos_dirent.name field.
265 * This ioctl allows umssync to rename a mangled file
266 * name before syncing it back in the EMD.
268 old_dentry = umsdos_lookup_dentry (dentry,
269 data.dos_dirent.d_name,
270 data.dos_dirent.d_reclen ,1);
271 ret = PTR_ERR(old_dentry);
272 if (IS_ERR(old_dentry))
273 goto out;
274 new_dentry = umsdos_lookup_dentry (dentry,
275 data.umsdos_dirent.name,
276 data.umsdos_dirent.name_len, 1);
277 ret = PTR_ERR(new_dentry);
278 if (!IS_ERR(new_dentry)) {
279 printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
280 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
281 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
282 ret = msdos_rename (dir, old_dentry, dir, new_dentry);
283 dput(new_dentry);
285 dput(old_dentry);
286 goto out;
288 else if (cmd == UMSDOS_UNLINK_EMD) {
289 /* #Specification: ioctl / UMSDOS_UNLINK_EMD
290 * The umsdos_dirent field of the struct umsdos_ioctl is used
291 * as is to remove an entry from the EMD of the directory.
292 * No validation is done (yet). The mode field is used
293 * to validate S_ISDIR or S_ISREG.
295 * Return 0 if success.
297 struct umsdos_info info;
299 /* This makes sure info.entry and info in general
300 * is correctly initialised
302 memcpy (&info.entry, &data.umsdos_dirent,
303 sizeof (data.umsdos_dirent));
304 umsdos_parse (data.umsdos_dirent.name,
305 data.umsdos_dirent.name_len, &info);
306 ret = umsdos_delentry (dentry, &info,
307 S_ISDIR (data.umsdos_dirent.mode));
308 if (ret) {
309 printk(KERN_WARNING
310 "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
311 dentry->d_name.name, info.entry.name, ret);
313 goto out;
315 else if (cmd == UMSDOS_UNLINK_DOS) {
316 struct dentry *temp;
318 /* #Specification: ioctl / UMSDOS_UNLINK_DOS
319 * The dos_dirent field of the struct umsdos_ioctl is used to
320 * execute a msdos_unlink operation. The d_name and d_reclen
321 * fields are used.
323 * Return 0 if success.
325 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
326 data.dos_dirent.d_reclen, 1);
327 ret = PTR_ERR(temp);
328 if (IS_ERR(temp))
329 goto out;
330 ret = -ENOENT;
331 if (temp->d_inode) {
332 ret = -EISDIR;
333 if (!S_ISDIR(temp->d_inode->i_mode))
334 ret = msdos_unlink (dir, temp);
336 dput (temp);
337 goto out;
339 else if (cmd == UMSDOS_RMDIR_DOS) {
340 struct dentry *temp;
342 /* #Specification: ioctl / UMSDOS_RMDIR_DOS
343 * The dos_dirent field of the struct umsdos_ioctl is used to
344 * execute a msdos_rmdir operation. The d_name and d_reclen
345 * fields are used.
347 * Return 0 if success.
349 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
350 data.dos_dirent.d_reclen, 1);
351 ret = PTR_ERR(temp);
352 if (IS_ERR(temp))
353 goto out;
354 ret = -ENOENT;
355 if (temp->d_inode) {
356 ret = -ENOTDIR;
357 if (S_ISDIR(temp->d_inode->i_mode))
358 ret = msdos_rmdir (dir, temp);
360 dput (temp);
361 goto out;
363 } else if (cmd == UMSDOS_STAT_DOS) {
364 /* #Specification: ioctl / UMSDOS_STAT_DOS
365 * The dos_dirent field of the struct umsdos_ioctl is
366 * used to execute a stat operation in the DOS directory.
367 * The d_name and d_reclen fields are used.
369 * The following field of umsdos_ioctl.stat are filled.
371 * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
372 * Return 0 if success.
374 struct dentry *dret;
375 struct inode *inode;
377 dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
378 data.dos_dirent.d_reclen, 1);
379 ret = PTR_ERR(dret);
380 if (IS_ERR(dret))
381 goto out;
382 ret = -ENOENT;
383 inode = dret->d_inode;
384 if (inode) {
385 data.stat.st_ino = inode->i_ino;
386 data.stat.st_mode = inode->i_mode;
387 data.stat.st_size = inode->i_size;
388 data.stat.st_atime = inode->i_atime;
389 data.stat.st_ctime = inode->i_ctime;
390 data.stat.st_mtime = inode->i_mtime;
391 ret = -EFAULT;
392 if (!copy_to_user (&idata->stat, &data.stat,
393 sizeof (data.stat)))
394 ret = 0;
396 dput(dret);
397 goto out;
399 else if (cmd == UMSDOS_DOS_SETUP) {
400 /* #Specification: ioctl / UMSDOS_DOS_SETUP
401 * The UMSDOS_DOS_SETUP ioctl allow changing the
402 * default permission of the MS-DOS filesystem driver
403 * on the fly. The MS-DOS driver applies global permissions
404 * to every file and directory. Normally these permissions
405 * are controlled by a mount option. This is not
406 * available for root partition, so a special utility
407 * (umssetup) is provided to do this, normally in
408 * /etc/rc.local.
410 * Be aware that this applies ONLY to MS-DOS directories
411 * (those without EMD --linux-.---). Umsdos directory
412 * have independent (standard) permission for each
413 * and every file.
415 * The field umsdos_dirent provide the information needed.
416 * umsdos_dirent.uid and gid sets the owner and group.
417 * umsdos_dirent.mode set the permissions flags.
419 dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
420 dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
421 dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
422 ret = 0;
424 out:
425 Printk (("ioctl %d, returning %d\n", cmd, ret));
426 return ret;