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