1 /*
2 * linux/fs/umsdos/emd.c
3 *
4 * Written 1993 by Jacques Gelinas
5 *
6 * Extended MS-DOS directory handling functions
7 */
8 #ifdef MODULE
9 #include <linux/module.h>
10 #endif
11
12 #include <linux/types.h>
13 #include <linux/fcntl.h>
14 #include <linux/kernel.h>
15 #include <asm/segment.h>
16 #include <linux/sched.h>
17 #include <linux/errno.h>
18 #include <linux/string.h>
19 #include <linux/msdos_fs.h>
20 #include <linux/umsdos_fs.h>
21
22 #define PRINTK(x)
23 #define Printk(x) printk x
24
25 int umsdos_readdir_kmem(
/* ![[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)
*/
26 struct inode *inode,
27 struct file *filp,
28 struct dirent *dirent,
29 int count)
30 {
31 int ret;
32 int old_fs = get_fs();
33 set_fs (KERNEL_DS);
34 ret = msdos_readdir(inode,filp,dirent,count);
35 set_fs (old_fs);
36 return ret;
37 }
38 /*
39 Read a file into kernel space memory
40 */
41 int umsdos_file_read_kmem(
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
42 struct inode *inode,
43 struct file *filp,
44 char *buf,
45 int count)
46 {
47 int ret;
48 int old_fs = get_fs();
49 set_fs (KERNEL_DS);
50 ret = msdos_file_read(inode,filp,buf,count);
51 set_fs (old_fs);
52 return ret;
53 }
54 /*
55 Write to a file from kernel space
56 */
57 int umsdos_file_write_kmem(
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
58 struct inode *inode,
59 struct file *filp,
60 char *buf,
61 int count)
62 {
63 int ret;
64 int old_fs = get_fs();
65 set_fs (KERNEL_DS);
66 ret = msdos_file_write(inode,filp,buf,count);
67 set_fs (old_fs);
68 return ret;
69 }
70
71
72 /*
73 Write a block of bytes into one EMD file.
74 The block of data is NOT in user space.
75
76 Return 0 if ok, a negative error code if not.
77 */
78 int umsdos_emd_dir_write (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
79 struct inode *emd_dir,
80 struct file *filp,
81 char *buf, /* buffer in kernel memory, not in user space */
82 int count)
83 {
84 int written;
85 filp->f_flags = 0;
86 written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
87 return written != count ? -EIO : 0;
88 }
89 /*
90 Read a block of bytes from one EMD file.
91 The block of data is NOT in user space.
92 Return 0 if ok, -EIO if any error.
93 */
94 int umsdos_emd_dir_read (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
95 struct inode *emd_dir,
96 struct file *filp,
97 char *buf, /* buffer in kernel memory, not in user space */
98 int count)
99 {
100 int ret = 0;
101 int sizeread;
102 filp->f_flags = 0;
103 sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
104 if (sizeread != count){
105 printk ("UMSDOS: problem with EMD file. Can't read\n");
106 ret = -EIO;
107 }
108 return ret;
109
110 }
111 /*
112 Locate the EMD file in a directory and optionally, creates it.
113
114 Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
115 */
116 struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
117 {
118 struct inode *ret = NULL;
119 if (dir->u.umsdos_i.i_emd_dir != 0){
120 ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
121 PRINTK (("deja trouve %d %x [%d] "
122 ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
123 }else{
124 umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
125 PRINTK (("emd_dir_lookup "));
126 if (ret != NULL){
127 PRINTK (("Find --linux "));
128 dir->u.umsdos_i.i_emd_dir = ret->i_ino;
129 }else if (creat){
130 int code;
131 PRINTK (("avant create "));
132 dir->i_count++;
133 code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
134 ,S_IFREG|0777,&ret);
135 PRINTK (("Creat EMD code %d ret %x ",code,ret));
136 if (ret != NULL){
137 dir->u.umsdos_i.i_emd_dir = ret->i_ino;
138 }else{
139 printk ("UMSDOS: Can't create EMD file\n");
140 }
141 }
142 }
143 if (ret != NULL){
144 /* Disable UMSDOS_notify_change() for EMD file */
145 ret->u.umsdos_i.i_emd_owner = 0xffffffff;
146 }
147 return ret;
148 }
149
150 /*
151 Read an entry from the EMD file.
152 Support variable length record.
153 Return -EIO if error, 0 if ok.
154 */
155 int umsdos_emd_dir_readentry (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
156 struct inode *emd_dir,
157 struct file *filp,
158 struct umsdos_dirent *entry)
159 {
160 int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
161 if (ret == 0){
162 /* Variable size record. Maybe, we have to read some more */
163 int recsize = umsdos_evalrecsize (entry->name_len);
164 if (recsize > UMSDOS_REC_SIZE){
165 ret = umsdos_emd_dir_read(emd_dir,filp
166 ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
167
168 }
169 }
170 return ret;
171 }
172 /*
173 Write an entry in the EMD file.
174 Return 0 if ok, -EIO if some error.
175 */
176 int umsdos_writeentry (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
177 struct inode *dir,
178 struct inode *emd_dir,
179 struct umsdos_info *info,
180 int free_entry) /* This entry is deleted, so Write all 0's */
181 {
182 int ret = 0;
183 struct file filp;
184 struct umsdos_dirent *entry = &info->entry;
185 struct umsdos_dirent entry0;
186 if (free_entry){
187 /* #Specification: EMD file / empty entries
188 Unused entry in the EMD file are identify
189 by the name_len field equal to 0. However to
190 help future extension (or bug correction :-( ),
191 empty entries are filled with 0.
192 */
193 memset (&entry0,0,sizeof(entry0));
194 entry = &entry0;
195 }else if (entry->name_len > 0){
196 memset (entry->name+entry->name_len,'\0'
197 ,sizeof(entry->name)-entry->name_len);
198 /* #Specification: EMD file / spare bytes
199 10 bytes are unused in each record of the EMD. They
200 are set to 0 all the time. So it will be possible
201 to do new stuff and rely on the state of those
202 bytes in old EMD file around.
203 */
204 memset (entry->spare,0,sizeof(entry->spare));
205 }
206 filp.f_pos = info->f_pos;
207 filp.f_reada = 0;
208 ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
209 if (ret != 0){
210 printk ("UMSDOS: problem with EMD file. Can't write\n");
211 }else{
212 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
213 dir->i_dirt = 1;
214 }
215 return ret;
216 }
217
218 #define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
219 struct find_buffer{
220 char buffer[CHUNK_SIZE];
221 int pos; /* read offset in buffer */
222 int size; /* Current size of buffer */
223 struct file filp;
224 };
225
226 /*
227 Fill the read buffer and take care of the byte remaining inside.
228 Unread bytes are simply move to the beginning.
229
230 Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
231 */
232 static int umsdos_fillbuf (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
233 struct inode *inode,
234 struct find_buffer *buf)
235 {
236 int ret = -ENOENT;
237 int mustmove = buf->size - buf->pos;
238 int mustread;
239 int remain;
240 if (mustmove > 0){
241 memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
242 }
243 buf->pos = 0;
244 mustread = CHUNK_SIZE - mustmove;
245 remain = inode->i_size - buf->filp.f_pos;
246 if (remain < mustread) mustread = remain;
247 if (mustread > 0){
248 ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
249 ,mustread);
250 if (ret == 0) buf->size = mustmove + mustread;
251 }else if (mustmove){
252 buf->size = mustmove;
253 ret = 0;
254 }
255 return ret;
256 }
257
258 /*
259 General search, locate a name in the EMD file or an empty slot to
260 store it. if info->entry.name_len == 0, search the first empty
261 slot (of the proper size).
262
263 Caller must do iput on *pt_emd_dir.
264
265 Return 0 if found, -ENOENT if not found, another error code if
266 other problem.
267
268 So this routine is used to either find an existing entry or to
269 create a new one, while making sure it is a new one. After you
270 get -ENOENT, you make sure the entry is stuffed correctly and
271 call umsdos_writeentry().
272
273 To delete an entry, you find it, zero out the entry (memset)
274 and call umsdos_writeentry().
275
276 All this to say that umsdos_writeentry must be call after this
277 function since it rely on the f_pos field of info.
278 */
279 static int umsdos_find (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
280 struct inode *dir,
281 struct umsdos_info *info, /* Hold name and name_len */
282 /* Will hold the entry found */
283 struct inode **pt_emd_dir) /* Will hold the emd_dir inode */
284 /* or NULL if not found */
285 {
286 /* #Specification: EMD file structure
287 The EMD file uses a fairly simple layout. It is made of records
288 (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
289 record, multiple contiguous record are allocated.
290 */
291 int ret = -ENOENT;
292 struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
293 if (emd_dir != NULL){
294 struct umsdos_dirent *entry = &info->entry;
295 int recsize = info->recsize;
296 struct {
297 off_t posok; /* Position available to store the entry */
298 int found; /* A valid empty position has been found */
299 off_t one; /* One empty position -> maybe <- large enough */
300 int onesize; /* size of empty region starting at one */
301 }empty;
302 /* Read several entries at a time to speed up the search */
303 struct find_buffer buf;
304 buf.pos = 0;
305 buf.size = 0;
306 buf.filp.f_pos = 0;
307 buf.filp.f_reada = 1;
308 empty.found = 0;
309 empty.posok = emd_dir->i_size;
310 empty.onesize = 0;
311 while (1){
312 struct umsdos_dirent *rentry = (struct umsdos_dirent*)
313 (buf.buffer + buf.pos);
314 int file_pos = buf.filp.f_pos - buf.size + buf.pos;
315 if (buf.pos == buf.size){
316 ret = umsdos_fillbuf (emd_dir,&buf);
317 if (ret < 0){
318 /* Not found, so note where it can be added */
319 info->f_pos = empty.posok;
320 break;
321 }
322 }else if (rentry->name_len == 0){
323 /* We are looking for an empty section at least */
324 /* recsize large */
325 if (entry->name_len == 0){
326 info->f_pos = file_pos;
327 ret = 0;
328 break;
329 }else if (!empty.found){
330 if (empty.onesize == 0){
331 /* This is the first empty record of a section */
332 empty.one = file_pos;
333 }
334 /* grow the empty section */
335 empty.onesize += UMSDOS_REC_SIZE;
336 if (empty.onesize == recsize){
337 /* here is a large enough section */
338 empty.posok = empty.one;
339 empty.found = 1;
340 }
341 }
342 buf.pos += UMSDOS_REC_SIZE;
343 }else{
344 int entry_size = umsdos_evalrecsize(rentry->name_len);
345 if (buf.pos+entry_size > buf.size){
346 ret = umsdos_fillbuf (emd_dir,&buf);
347 if (ret < 0){
348 /* Not found, so note where it can be added */
349 info->f_pos = empty.posok;
350 break;
351 }
352 }else{
353 empty.onesize = 0; /* Reset the free slot search */
354 if (entry->name_len == rentry->name_len
355 && memcmp(entry->name,rentry->name,rentry->name_len)
356 ==0){
357 info->f_pos = file_pos;
358 *entry = *rentry;
359 ret = 0;
360 break;
361 }else{
362 buf.pos += entry_size;
363 }
364 }
365 }
366 }
367 umsdos_manglename(info);
368 }
369 *pt_emd_dir = emd_dir;
370 return ret;
371 }
372 /*
373 Add a new entry in the emd file
374 Return 0 if ok or a negative error code.
375 Return -EEXIST if the entry already exist.
376
377 Complete the information missing in info.
378 */
379 int umsdos_newentry (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
380 struct inode *dir,
381 struct umsdos_info *info)
382 {
383 struct inode *emd_dir;
384 int ret = umsdos_find (dir,info,&emd_dir);
385 if (ret == 0){
386 ret = -EEXIST;
387 }else if (ret == -ENOENT){
388 ret = umsdos_writeentry(dir,emd_dir,info,0);
389 PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
390 }
391 iput (emd_dir);
392 return ret;
393 }
394 /*
395 Create a new hidden link.
396 Return 0 if ok, an error code if not.
397 */
398 int umsdos_newhidden (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
399 struct inode *dir,
400 struct umsdos_info *info)
401 {
402 struct inode *emd_dir;
403 int ret;
404 umsdos_parse ("..LINK",6,info);
405 info->entry.name_len = 0;
406 ret = umsdos_find (dir,info,&emd_dir);
407 iput (emd_dir);
408 if (ret == -ENOENT || ret == 0){
409 /* #Specification: hard link / hidden name
410 When a hard link is created, the original file is renamed
411 to a hidden name. The name is "..LINKNNN" where NNN is a
412 number define from the entry offset in the EMD file.
413 */
414 info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
415 ,info->f_pos);
416 ret = 0;
417 }
418 return ret;
419 }
420 /*
421 Remove an entry from the emd file
422 Return 0 if ok, a negative error code otherwise.
423
424 Complete the information missing in info.
425 */
426 int umsdos_delentry (
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
427 struct inode *dir,
428 struct umsdos_info *info,
429 int isdir)
430 {
431 struct inode *emd_dir;
432 int ret = umsdos_find (dir,info,&emd_dir);
433 if (ret == 0){
434 if (info->entry.name_len != 0){
435 if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
436 if (S_ISDIR(info->entry.mode)){
437 ret = -EISDIR;
438 }else{
439 ret = -ENOTDIR;
440 }
441 }else{
442 ret = umsdos_writeentry(dir,emd_dir,info,1);
443 }
444 }
445 }
446 iput(emd_dir);
447 return ret;
448 }
449
450
451 /*
452 Verify is a EMD directory is empty.
453 Return 0 if not empty
454 1 if empty
455 2 if empty, no EMD file.
456 */
457 int umsdos_isempty (struct inode *dir)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
458 {
459 int ret = 2;
460 struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
461 /* If the EMD file does not exist, it is certainly empty :-) */
462 if (emd_dir != NULL){
463 struct file filp;
464 /* Find an empty slot */
465 filp.f_pos = 0;
466 filp.f_reada = 1;
467 filp.f_flags = O_RDONLY;
468 ret = 1;
469 while (filp.f_pos < emd_dir->i_size){
470 struct umsdos_dirent entry;
471 if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
472 ret = 0;
473 break;
474 }else if (entry.name_len != 0){
475 ret = 0;
476 break;
477 }
478 }
479 iput (emd_dir);
480 }
481 return ret;
482 }
483
484 /*
485 Locate an entry in a EMD directory.
486 Return 0 if ok, errcod if not, generally -ENOENT.
487 */
488 int umsdos_findentry (
/* ![[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)
*/
489 struct inode *dir,
490 struct umsdos_info *info,
491 int expect) /* 0: anything */
492 /* 1: file */
493 /* 2: directory */
494 {
495 struct inode *emd_dir;
496 int ret = umsdos_find (dir,info,&emd_dir);
497 if (ret == 0){
498 if (expect != 0){
499 if (S_ISDIR(info->entry.mode)){
500 if (expect != 2) ret = -EISDIR;
501 }else if (expect == 2){
502 ret = -ENOTDIR;
503 }
504 }
505 }
506 iput (emd_dir);
507 return ret;
508 }
509