1 /*
2 * linux/fs/umsdos/ioctl.c
3 *
4 * Written 1993 by Jacques Gelinas
5 *
6 * Extended MS-DOS ioctl directory handling functions
7 */
8 #ifdef MODULE
9 #include <linux/module.h>
10 #endif
11
12 #include <asm/segment.h>
13 #include <linux/errno.h>
14 #include <linux/mm.h>
15 #include <linux/kernel.h>
16 #include <linux/sched.h>
17 #include <linux/fs.h>
18 #include <linux/msdos_fs.h>
19 #include <linux/umsdos_fs.h>
20
21 #define PRINTK(x)
22 #define Printk(x) printk x
23
24 struct UMSDOS_DIR_ONCE {
25 struct dirent *ent;
26 int count;
27 };
28
29 /*
30 Record a single entry the first call.
31 Return -EINVAL the next one.
32 */
33 static int umsdos_ioctl_fill(
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
34 void * buf,
35 const char * name,
36 int name_len,
37 off_t offset,
38 ino_t ino)
39 {
40 int ret = -EINVAL;
41 struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
42 if (d->count == 0){
43 memcpy_tofs (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);
48 d->count = 1;
49 ret = 0;
50 }
51 return ret;
52 }
53
54
55 /*
56 Perform special function on a directory
57 */
58 int UMSDOS_ioctl_dir (
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
59 struct inode *dir,
60 struct file *filp,
61 unsigned int cmd,
62 unsigned long data)
63 {
64 int ret = -EPERM;
65 /* #Specification: ioctl / acces
66 Only root (effective id) is allowed to do IOCTL on directory
67 in UMSDOS. EPERM is returned for other user.
68 */
69 /*
70 Well, not all case require write access, but it simplify the code
71 and let's face it, there is only one client (umssync) for all this
72 */
73 if (verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
74 ret = -EFAULT;
75 }else if (current->euid == 0
76 || cmd == UMSDOS_GETVERSION){
77 struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
78 ret = -EINVAL;
79 /* #Specification: ioctl / prototypes
80 The official prototype for the umsdos ioctl on directory
81 is:
82
83 int ioctl (
84 int fd, // File handle of the directory
85 int cmd, // command
86 struct umsdos_ioctl *data)
87
88 The struct and the commands are defined in linux/umsdos_fs.h.
89
90 umsdos_progs/umsdosio.c provide an interface in C++ to all
91 these ioctl. umsdos_progs/udosctl is a small utility showing
92 all this.
93
94 These ioctl generally allow one to work on the EMD or the
95 DOS directory independently. These are essential to implement
96 the synchronise.
97 */
98 PRINTK (("ioctl %d ",cmd));
99 if (cmd == UMSDOS_GETVERSION){
100 /* #Specification: ioctl / UMSDOS_GETVERSION
101 The field version and release of the structure
102 umsdos_ioctl are filled with the version and release
103 number of the fs code in the kernel. This will allow
104 some form of checking. Users won't be able to run
105 incompatible utility such as the synchroniser (umssync).
106 umsdos_progs/umsdosio.c enforce this checking.
107
108 Return always 0.
109 */
110 put_fs_byte (UMSDOS_VERSION,&idata->version);
111 put_fs_byte (UMSDOS_RELEASE,&idata->release);
112 ret = 0;
113 }else if (cmd == UMSDOS_READDIR_DOS){
114 /* #Specification: ioctl / UMSDOS_READDIR_DOS
115 One entry is read from the DOS directory at the current
116 file position. The entry is put as is in the dos_dirent
117 field of struct umsdos_ioctl.
118
119 Return > 0 if success.
120 */
121 struct UMSDOS_DIR_ONCE bufk;
122 bufk.count = 0;
123 bufk.ent = &idata->dos_dirent;
124 msdos_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
125 ret = bufk.count == 1 ? 1 : 0;
126 }else if (cmd == UMSDOS_READDIR_EMD){
127 /* #Specification: ioctl / UMSDOS_READDIR_EMD
128 One entry is read from the EMD at the current
129 file position. The entry is put as is in the umsdos_dirent
130 field of struct umsdos_ioctl. The corresponding mangled
131 DOS entry name is put in the dos_dirent field.
132
133 All entries are read including hidden links. Blank
134 entries are skipped.
135
136 Return > 0 if success.
137 */
138 struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
139 if (emd_dir != NULL){
140 while (1){
141 if (filp->f_pos >= emd_dir->i_size){
142 ret = 0;
143 break;
144 }else{
145 struct umsdos_dirent entry;
146 off_t f_pos = filp->f_pos;
147 ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
148 if (ret < 0){
149 break;
150 }else if (entry.name_len > 0){
151 struct umsdos_info info;
152 ret = entry.name_len;
153 umsdos_parse (entry.name,entry.name_len,&info);
154 info.f_pos = f_pos;
155 umsdos_manglename(&info);
156 memcpy_tofs(&idata->umsdos_dirent,&entry
157 ,sizeof(entry));
158 memcpy_tofs(&idata->dos_dirent.d_name
159 ,info.fake.fname,info.fake.len+1);
160 break;
161 }
162 }
163 }
164 iput (emd_dir);
165 }else{
166 /* The absence of the EMD is simply seen as an EOF */
167 ret = 0;
168 }
169 }else if (cmd == UMSDOS_INIT_EMD){
170 /* #Specification: ioctl / UMSDOS_INIT_EMD
171 The UMSDOS_INIT_EMD command make sure the EMD
172 exist for a directory. If it does not, it is
173 created. Also, it makes sure the directory functions
174 table (struct inode_operations) is set to the UMSDOS
175 semantic. This mean that umssync may be applied to
176 an "opened" msdos directory, and it will change behavior
177 on the fly.
178
179 Return 0 if success.
180 */
181 extern struct inode_operations umsdos_rdir_inode_operations;
182 struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
183 ret = emd_dir != NULL;
184 iput (emd_dir);
185
186 dir->i_op = ret
187 ? &umsdos_dir_inode_operations
188 : &umsdos_rdir_inode_operations;
189 }else{
190 struct umsdos_ioctl data;
191 memcpy_fromfs (&data,idata,sizeof(data));
192 if (cmd == UMSDOS_CREAT_EMD){
193 /* #Specification: ioctl / UMSDOS_CREAT_EMD
194 The umsdos_dirent field of the struct umsdos_ioctl is used
195 as is to create a new entry in the EMD of the directory.
196 The DOS directory is not modified.
197 No validation is done (yet).
198
199 Return 0 if success.
200 */
201 struct umsdos_info info;
202 /* This makes sure info.entry and info in general is correctly */
203 /* initialised */
204 memcpy (&info.entry,&data.umsdos_dirent
205 ,sizeof(data.umsdos_dirent));
206 umsdos_parse (data.umsdos_dirent.name
207 ,data.umsdos_dirent.name_len,&info);
208 ret = umsdos_newentry (dir,&info);
209 }else if (cmd == UMSDOS_RENAME_DOS){
210 /* #Specification: ioctl / UMSDOS_RENAME_DOS
211 A file or directory is rename in a DOS directory
212 (not moved across directory). The source name
213 is in the dos_dirent.name field and the destination
214 is in umsdos_dirent.name field.
215
216 This ioctl allows umssync to rename a mangle file
217 name before syncing it back in the EMD.
218 */
219 dir->i_count += 2;
220 ret = msdos_rename (dir
221 ,data.dos_dirent.d_name,data.dos_dirent.d_reclen
222 ,dir
223 ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
224 }else if (cmd == UMSDOS_UNLINK_EMD){
225 /* #Specification: ioctl / UMSDOS_UNLINK_EMD
226 The umsdos_dirent field of the struct umsdos_ioctl is used
227 as is to remove an entry from the EMD of the directory.
228 No validation is done (yet). The mode field is used
229 to validate S_ISDIR or S_ISREG.
230
231 Return 0 if success.
232 */
233 struct umsdos_info info;
234 /* This makes sure info.entry and info in general is correctly */
235 /* initialised */
236 memcpy (&info.entry,&data.umsdos_dirent
237 ,sizeof(data.umsdos_dirent));
238 umsdos_parse (data.umsdos_dirent.name
239 ,data.umsdos_dirent.name_len,&info);
240 ret = umsdos_delentry (dir,&info
241 ,S_ISDIR(data.umsdos_dirent.mode));
242 }else if (cmd == UMSDOS_UNLINK_DOS){
243 /* #Specification: ioctl / UMSDOS_UNLINK_DOS
244 The dos_dirent field of the struct umsdos_ioctl is used to
245 execute a msdos_unlink operation. The d_name and d_reclen
246 fields are used.
247
248 Return 0 if success.
249 */
250 dir->i_count++;
251 ret = msdos_unlink (dir,data.dos_dirent.d_name
252 ,data.dos_dirent.d_reclen);
253 }else if (cmd == UMSDOS_RMDIR_DOS){
254 /* #Specification: ioctl / UMSDOS_RMDIR_DOS
255 The dos_dirent field of the struct umsdos_ioctl is used to
256 execute a msdos_unlink operation. The d_name and d_reclen
257 fields are used.
258
259 Return 0 if success.
260 */
261 dir->i_count++;
262 ret = msdos_rmdir (dir,data.dos_dirent.d_name
263 ,data.dos_dirent.d_reclen);
264 }else if (cmd == UMSDOS_STAT_DOS){
265 /* #Specification: ioctl / UMSDOS_STAT_DOS
266 The dos_dirent field of the struct umsdos_ioctl is
267 used to execute a stat operation in the DOS directory.
268 The d_name and d_reclen fields are used.
269
270 The following field of umsdos_ioctl.stat are filled.
271
272 st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
273 Return 0 if success.
274 */
275 struct inode *inode;
276 ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
277 ,data.dos_dirent.d_reclen,&inode);
278 if (ret == 0){
279 data.stat.st_ino = inode->i_ino;
280 data.stat.st_mode = inode->i_mode;
281 data.stat.st_size = inode->i_size;
282 data.stat.st_atime = inode->i_atime;
283 data.stat.st_ctime = inode->i_ctime;
284 data.stat.st_mtime = inode->i_mtime;
285 memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
286 iput (inode);
287 }
288 }else if (cmd == UMSDOS_DOS_SETUP){
289 /* #Specification: ioctl / UMSDOS_DOS_SETUP
290 The UMSDOS_DOS_SETUP ioctl allow changing the
291 default permission of the MsDOS file system driver
292 on the fly. The MsDOS driver apply global permission
293 to every file and directory. Normally these permissions
294 are controlled by a mount option. This is not
295 available for root partition, so a special utility
296 (umssetup) is provided to do this, normally in
297 /etc/rc.local.
298
299 Be aware that this apply ONLY to MsDOS directory
300 (those without EMD --linux-.---). Umsdos directory
301 have independent (standard) permission for each
302 and every file.
303
304 The field umsdos_dirent provide the information needed.
305 umsdos_dirent.uid and gid sets the owner and group.
306 umsdos_dirent.mode set the permissions flags.
307 */
308 dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid;
309 dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
310 dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
311 ret = 0;
312 }
313 }
314 }
315 PRINTK (("ioctl return %d\n",ret));
316 return ret;
317 }
318
319
320