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