2 * linux/fs/umsdos/ioctl.c
4 * Written 1993 by Jacques Gelinas
6 * Extended MS-DOS ioctl directory handling functions
9 * 11/07/2003 Daniele Bellucci <bellucda@tiscali.it>
10 * - audit copy_to_user/put_user in umsdos_ioctl_fill.
13 #include <asm/uaccess.h>
14 #include <linux/errno.h>
16 #include <linux/kernel.h>
17 #include <linux/time.h>
19 #include <linux/msdos_fs.h>
20 #include <linux/umsdos_fs.h>
22 struct UMSDOS_DIR_ONCE
{
28 * Record a single entry the first call.
29 * Return -EINVAL the next one.
31 static int umsdos_ioctl_fill (
40 struct UMSDOS_DIR_ONCE
*d
= (struct UMSDOS_DIR_ONCE
*) buf
;
43 if (copy_to_user (d
->ent
->d_name
, name
, name_len
) ||
44 put_user ('\0', d
->ent
->d_name
+ name_len
) ||
45 put_user (name_len
, &d
->ent
->d_reclen
) ||
46 put_user (ino
, &d
->ent
->d_ino
) ||
47 put_user (offset
, &d
->ent
->d_off
))
57 * Perform special function on a directory
59 /* #Specification: ioctl / prototypes
60 * The official prototype for the umsdos ioctl on directory
64 * int fd, // File handle of the directory
66 * struct umsdos_ioctl *data)
68 * The struct and the commands are defined in linux/umsdos_fs.h.
70 * umsdos_progs/umsdosio.c provide an interface in C++ to all
71 * these ioctl. umsdos_progs/udosctl is a small utility showing
74 * These ioctl generally allow one to work on the EMD or the
75 * DOS directory independently. These are essential to implement
78 int UMSDOS_ioctl_dir(struct inode
*dir
, struct file
*filp
, unsigned int cmd
,
79 unsigned long data_ptr
)
81 struct dentry
*dentry
= filp
->f_dentry
;
82 struct umsdos_ioctl
*idata
= (struct umsdos_ioctl
*) data_ptr
;
84 struct umsdos_ioctl data
;
86 Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
87 dentry
->d_parent
->d_name
.name
, dentry
->d_name
.name
, cmd
, data_ptr
));
89 /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
90 if (cmd
!= UMSDOS_GETVERSION
91 && cmd
!= UMSDOS_READDIR_DOS
92 && cmd
!= UMSDOS_READDIR_EMD
93 && cmd
!= UMSDOS_INIT_EMD
94 && cmd
!= UMSDOS_CREAT_EMD
95 && cmd
!= UMSDOS_RENAME_DOS
96 && cmd
!= UMSDOS_UNLINK_EMD
97 && cmd
!= UMSDOS_UNLINK_DOS
98 && cmd
!= UMSDOS_RMDIR_DOS
99 && cmd
!= UMSDOS_STAT_DOS
100 && cmd
!= UMSDOS_DOS_SETUP
)
101 return fat_dir_ioctl (dir
, filp
, cmd
, data_ptr
);
103 /* #Specification: ioctl / access
104 * Only root (effective id) is allowed to do IOCTL on directory
105 * in UMSDOS. EPERM is returned for other user.
108 * Well, not all cases require write access, but it simplifies
109 * the code, and let's face it, there is only one client (umssync)
112 ret
= verify_area (VERIFY_WRITE
, (void *) data_ptr
,
113 sizeof (struct umsdos_ioctl
));
118 if (current
->euid
!= 0 && cmd
!= UMSDOS_GETVERSION
)
122 if (cmd
== UMSDOS_GETVERSION
) {
123 /* #Specification: ioctl / UMSDOS_GETVERSION
124 * The field version and release of the structure
125 * umsdos_ioctl are filled with the version and release
126 * number of the fs code in the kernel. This will allow
127 * some form of checking. Users won't be able to run
128 * incompatible utility such as the synchroniser (umssync).
129 * umsdos_progs/umsdosio.c enforce this checking.
133 put_user (UMSDOS_VERSION
, &idata
->version
);
134 put_user (UMSDOS_RELEASE
, &idata
->release
);
138 if (cmd
== UMSDOS_READDIR_DOS
) {
139 /* #Specification: ioctl / UMSDOS_READDIR_DOS
140 * One entry is read from the DOS directory at the current
141 * file position. The entry is put as is in the dos_dirent
142 * field of struct umsdos_ioctl.
144 * Return > 0 if success.
146 struct UMSDOS_DIR_ONCE bufk
;
149 bufk
.ent
= &idata
->dos_dirent
;
151 fat_readdir (filp
, &bufk
, umsdos_ioctl_fill
);
153 ret
= bufk
.count
== 1 ? 1 : 0;
156 if (cmd
== UMSDOS_READDIR_EMD
) {
157 /* #Specification: ioctl / UMSDOS_READDIR_EMD
158 * One entry is read from the EMD at the current
159 * file position. The entry is put as is in the umsdos_dirent
160 * field of struct umsdos_ioctl. The corresponding mangled
161 * DOS entry name is put in the dos_dirent field.
163 * All entries are read including hidden links. Blank
164 * entries are skipped.
166 * Return > 0 if success.
169 loff_t pos
= filp
->f_pos
;
171 /* The absence of the EMD is simply seen as an EOF */
172 demd
= umsdos_get_emd_dentry(dentry
);
180 while (pos
< demd
->d_inode
->i_size
) {
182 struct umsdos_dirent entry
;
183 struct umsdos_info info
;
185 ret
= umsdos_emd_dir_readentry (demd
, &pos
, &entry
);
187 if (ret
== -ENAMETOOLONG
) {
188 printk (KERN_INFO
"Fixing EMD entry with invalid size -- zeroing out\n");
189 memset (&info
, 0, sizeof (info
));
191 info
.recsize
= UMSDOS_REC_SIZE
;
192 ret
= umsdos_writeentry (dentry
, &info
, 1);
198 if (entry
.name_len
<= 0)
201 umsdos_parse (entry
.name
, entry
.name_len
, &info
);
203 umsdos_manglename (&info
);
205 if (copy_to_user (&idata
->umsdos_dirent
, &entry
,
208 if (copy_to_user (&idata
->dos_dirent
.d_name
,
212 ret
= entry
.name_len
;
215 /* update the original f_pos */
222 if (cmd
== UMSDOS_INIT_EMD
) {
223 /* #Specification: ioctl / UMSDOS_INIT_EMD
224 * The UMSDOS_INIT_EMD command makes sure the EMD
225 * exists for a directory. If it does not, it is
226 * created. Also, it makes sure the directory function
227 * table (struct inode_operations) is set to the UMSDOS
228 * semantic. This mean that umssync may be applied to
229 * an "opened" msdos directory, and it will change behavior
232 * Return 0 if success.
235 ret
= umsdos_make_emd(dentry
);
236 Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
237 dentry
->d_parent
->d_name
.name
, dentry
->d_name
.name
, ret
));
238 umsdos_setup_dir (dentry
);
243 if (copy_from_user (&data
, idata
, sizeof (data
)))
246 if (cmd
== UMSDOS_CREAT_EMD
) {
247 /* #Specification: ioctl / UMSDOS_CREAT_EMD
248 * The umsdos_dirent field of the struct umsdos_ioctl is used
249 * as is to create a new entry in the EMD of the directory.
250 * The DOS directory is not modified.
251 * No validation is done (yet).
253 * Return 0 if success.
255 struct umsdos_info info
;
257 /* This makes sure info.entry and info in general
258 * is correctly initialised
260 memcpy (&info
.entry
, &data
.umsdos_dirent
,
261 sizeof (data
.umsdos_dirent
));
262 umsdos_parse (data
.umsdos_dirent
.name
263 ,data
.umsdos_dirent
.name_len
, &info
);
264 ret
= umsdos_newentry (dentry
, &info
);
267 else if (cmd
== UMSDOS_RENAME_DOS
) {
268 struct dentry
*old_dentry
, *new_dentry
; /* FIXME */
270 /* #Specification: ioctl / UMSDOS_RENAME_DOS
271 * A file or directory is renamed in a DOS directory
272 * (not moved across directory). The source name
273 * is in the dos_dirent.name field and the destination
274 * is in umsdos_dirent.name field.
276 * This ioctl allows umssync to rename a mangled file
277 * name before syncing it back in the EMD.
279 old_dentry
= umsdos_lookup_dentry (dentry
,
280 data
.dos_dirent
.d_name
,
281 data
.dos_dirent
.d_reclen
,1);
282 ret
= PTR_ERR(old_dentry
);
283 if (IS_ERR(old_dentry
))
285 new_dentry
= umsdos_lookup_dentry (dentry
,
286 data
.umsdos_dirent
.name
,
287 data
.umsdos_dirent
.name_len
, 1);
288 ret
= PTR_ERR(new_dentry
);
289 if (!IS_ERR(new_dentry
)) {
290 printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
291 old_dentry
->d_parent
->d_name
.name
, old_dentry
->d_name
.name
,
292 new_dentry
->d_parent
->d_name
.name
, new_dentry
->d_name
.name
);
293 ret
= msdos_rename (dir
, old_dentry
, dir
, new_dentry
);
301 else if (cmd
== UMSDOS_UNLINK_EMD
) {
302 /* #Specification: ioctl / UMSDOS_UNLINK_EMD
303 * The umsdos_dirent field of the struct umsdos_ioctl is used
304 * as is to remove an entry from the EMD of the directory.
305 * No validation is done (yet). The mode field is used
306 * to validate S_ISDIR or S_ISREG.
308 * Return 0 if success.
310 struct umsdos_info info
;
312 /* This makes sure info.entry and info in general
313 * is correctly initialised
315 memcpy (&info
.entry
, &data
.umsdos_dirent
,
316 sizeof (data
.umsdos_dirent
));
317 umsdos_parse (data
.umsdos_dirent
.name
,
318 data
.umsdos_dirent
.name_len
, &info
);
319 ret
= umsdos_delentry (dentry
, &info
,
320 S_ISDIR (data
.umsdos_dirent
.mode
));
323 "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
324 dentry
->d_name
.name
, info
.entry
.name
, ret
);
328 else if (cmd
== UMSDOS_UNLINK_DOS
) {
331 /* #Specification: ioctl / UMSDOS_UNLINK_DOS
332 * The dos_dirent field of the struct umsdos_ioctl is used to
333 * execute a msdos_unlink operation. The d_name and d_reclen
336 * Return 0 if success.
338 temp
= umsdos_lookup_dentry(dentry
, data
.dos_dirent
.d_name
,
339 data
.dos_dirent
.d_reclen
, 1);
346 if (!S_ISDIR(temp
->d_inode
->i_mode
))
347 ret
= msdos_unlink (dir
, temp
);
354 else if (cmd
== UMSDOS_RMDIR_DOS
) {
357 /* #Specification: ioctl / UMSDOS_RMDIR_DOS
358 * The dos_dirent field of the struct umsdos_ioctl is used to
359 * execute a msdos_rmdir operation. The d_name and d_reclen
362 * Return 0 if success.
364 temp
= umsdos_lookup_dentry(dentry
, data
.dos_dirent
.d_name
,
365 data
.dos_dirent
.d_reclen
, 1);
372 if (S_ISDIR(temp
->d_inode
->i_mode
))
373 ret
= msdos_rmdir (dir
, temp
);
380 } else if (cmd
== UMSDOS_STAT_DOS
) {
381 /* #Specification: ioctl / UMSDOS_STAT_DOS
382 * The dos_dirent field of the struct umsdos_ioctl is
383 * used to execute a stat operation in the DOS directory.
384 * The d_name and d_reclen fields are used.
386 * The following field of umsdos_ioctl.stat are filled.
388 * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
389 * Return 0 if success.
394 dret
= umsdos_lookup_dentry(dentry
, data
.dos_dirent
.d_name
,
395 data
.dos_dirent
.d_reclen
, 1);
400 inode
= dret
->d_inode
;
402 data
.stat
.st_ino
= inode
->i_ino
;
403 data
.stat
.st_mode
= inode
->i_mode
;
404 data
.stat
.st_size
= inode
->i_size
;
405 data
.stat
.st_atime
= inode
->i_atime
;
406 data
.stat
.st_ctime
= inode
->i_ctime
;
407 data
.stat
.st_mtime
= inode
->i_mtime
;
409 if (!copy_to_user (&idata
->stat
, &data
.stat
,
416 else if (cmd
== UMSDOS_DOS_SETUP
) {
417 /* #Specification: ioctl / UMSDOS_DOS_SETUP
418 * The UMSDOS_DOS_SETUP ioctl allow changing the
419 * default permission of the MS-DOS filesystem driver
420 * on the fly. The MS-DOS driver applies global permissions
421 * to every file and directory. Normally these permissions
422 * are controlled by a mount option. This is not
423 * available for root partition, so a special utility
424 * (umssetup) is provided to do this, normally in
427 * Be aware that this applies ONLY to MS-DOS directories
428 * (those without EMD --linux-.---). Umsdos directory
429 * have independent (standard) permission for each
432 * The field umsdos_dirent provide the information needed.
433 * umsdos_dirent.uid and gid sets the owner and group.
434 * umsdos_dirent.mode set the permissions flags.
436 dir
->i_sb
->u
.msdos_sb
.options
.fs_uid
= data
.umsdos_dirent
.uid
;
437 dir
->i_sb
->u
.msdos_sb
.options
.fs_gid
= data
.umsdos_dirent
.gid
;
438 dir
->i_sb
->u
.msdos_sb
.options
.fs_fmask
=
439 dir
->i_sb
->u
.msdos_sb
.options
.fs_dmask
=
440 data
.umsdos_dirent
.mode
;
444 Printk (("ioctl %d, returning %d\n", cmd
, ret
));