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