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