~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/umsdos/emd.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 #include <linux/dcache.h>
 18 #include <linux/pagemap.h>
 19 
 20 #include <asm/delay.h>
 21 
 22 static void copy_entry(struct umsdos_dirent *p, struct umsdos_dirent *q)
 23 {
 24         p->name_len = q->name_len;
 25         p->name[p->name_len]='\0';
 26         p->flags = q->flags;
 27         p->nlink = le16_to_cpu (q->nlink);
 28         /* FIXME -- 32bit UID/GID issues */
 29         p->uid = le16_to_cpu (q->uid);
 30         p->gid = le16_to_cpu (q->gid);
 31         p->atime = le32_to_cpu (q->atime);
 32         p->mtime = le32_to_cpu (q->mtime);
 33         p->ctime = le32_to_cpu (q->ctime);
 34         p->rdev = le16_to_cpu (q->rdev);
 35         p->mode = le16_to_cpu (q->mode);
 36 }
 37 
 38 /*
 39  * Lookup the EMD dentry for a directory.
 40  *
 41  * Note: the caller must hold a lock on the parent directory.
 42  */
 43 struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
 44 {
 45         struct dentry *demd;
 46 
 47         demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE, 
 48                                         UMSDOS_EMD_NAMELEN, 1);
 49         return demd;
 50 }
 51 
 52 /*
 53  * Check whether a directory has an EMD file.
 54  *
 55  * Note: the caller must hold a lock on the parent directory.
 56  */
 57 int umsdos_have_emd(struct dentry *dir)
 58 {
 59         struct dentry *demd = umsdos_get_emd_dentry (dir);
 60         int found = 0;
 61 
 62         if (!IS_ERR(demd)) {
 63                 if (demd->d_inode)
 64                         found = 1;
 65                 dput(demd);
 66         }
 67         return found;
 68 }
 69 
 70 /*
 71  * Create the EMD file for a directory if it doesn't
 72  * already exist. Returns 0 or an error code.
 73  *
 74  * Note: the caller must hold a lock on the parent directory.
 75  */
 76 int umsdos_make_emd(struct dentry *parent)
 77 {
 78         struct dentry *demd = umsdos_get_emd_dentry(parent);
 79         int err = PTR_ERR(demd);
 80 
 81         if (IS_ERR(demd)) {
 82                 printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
 83                         parent->d_name.name, err);
 84                 goto out;
 85         }
 86 
 87         /* already created? */
 88         err = 0;
 89         if (demd->d_inode)
 90                 goto out_set;
 91 
 92 Printk(("umsdos_make_emd: creating EMD %s/%s\n",
 93 parent->d_name.name, demd->d_name.name));
 94 
 95         err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
 96         if (err) {
 97                 printk (KERN_WARNING
 98                         "umsdos_make_emd: create %s/%s failed, err=%d\n",
 99                         parent->d_name.name, demd->d_name.name, err);
100         }
101 out_set:
102         dput(demd);
103 out:
104         return err;
105 }
106 
107 
108 /*
109  * Read an entry from the EMD file.
110  * Support variable length record.
111  * Return -EIO if error, 0 if OK.
112  *
113  * does not change {d,i}_count
114  */
115 
116 int umsdos_emd_dir_readentry (struct dentry *demd, loff_t *pos, struct umsdos_dirent *entry)
117 {
118         struct address_space *mapping = demd->d_inode->i_mapping;
119         struct page *page;
120         struct umsdos_dirent *p;
121         int offs = *pos & ~PAGE_CACHE_MASK;
122         int recsize;
123         int ret = 0;
124 
125         page = read_cache_page(mapping, *pos>>PAGE_CACHE_SHIFT,
126                         (filler_t*)mapping->a_ops->readpage, NULL);
127         if (IS_ERR(page))
128                 goto sync_fail;
129         wait_on_page(page);
130         if (!Page_Uptodate(page))
131                 goto async_fail;
132         p = (struct umsdos_dirent*)(kmap(page)+offs);
133 
134         /* if this is an invalid entry (invalid name length), ignore it */
135         if( p->name_len > UMSDOS_MAXNAME )
136         {
137                 printk (KERN_WARNING "Ignoring invalid EMD entry with size %d\n", entry->name_len);
138                 p->name_len = 0; 
139                 ret = -ENAMETOOLONG; /* notify umssync(8) code that something is wrong */
140         }
141 
142         recsize = umsdos_evalrecsize(p->name_len);
143         if (offs + recsize > PAGE_CACHE_SIZE) {
144                 struct page *page2;
145                 int part = (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare;
146                 page2 = read_cache_page(mapping, 1+(*pos>>PAGE_CACHE_SHIFT),
147                                 (filler_t*)mapping->a_ops->readpage, NULL);
148                 if (IS_ERR(page2)) {
149                         kunmap(page);
150                         page_cache_release(page);
151                         page = page2;
152                         goto sync_fail;
153                 }
154                 wait_on_page(page2);
155                 if (!Page_Uptodate(page2)) {
156                         kunmap(page);
157                         page_cache_release(page2);
158                         goto async_fail;
159                 }
160                 memcpy(entry->spare,p->spare,part);
161                 memcpy(entry->spare+part,kmap(page2),
162                                 recsize+offs-PAGE_CACHE_SIZE);
163                 kunmap(page2);
164                 page_cache_release(page2);
165         } else
166                 memcpy(entry->spare,p->spare,((char*)p+recsize)-p->spare);
167         copy_entry(entry, p);
168         kunmap(page);
169         page_cache_release(page);
170         *pos += recsize;
171         return ret;
172 async_fail:
173         page_cache_release(page);
174         page = ERR_PTR(-EIO);
175 sync_fail:
176         return PTR_ERR(page);
177 }
178 
179 
180 /*
181  * Write an entry in the EMD file.
182  * Return 0 if OK, -EIO if some error.
183  *
184  * Note: the caller must hold a lock on the parent directory.
185  */
186 int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
187                                 int free_entry)
188 {
189         struct inode *dir = parent->d_inode;
190         struct umsdos_dirent *entry = &info->entry;
191         struct dentry *emd_dentry;
192         int ret;
193         struct umsdos_dirent entry0,*p;
194         struct address_space *mapping;
195         struct page *page, *page2 = NULL;
196         int offs;
197 
198         emd_dentry = umsdos_get_emd_dentry(parent);
199         ret = PTR_ERR(emd_dentry);
200         if (IS_ERR(emd_dentry))
201                 goto out;
202         /* make sure there's an EMD file */
203         ret = -EIO;
204         if (!emd_dentry->d_inode) {
205                 printk(KERN_WARNING
206                         "umsdos_writeentry: no EMD file in %s/%s\n",
207                         parent->d_parent->d_name.name, parent->d_name.name);
208                 goto out_dput;
209         }
210 
211         if (free_entry) {
212                 /* #Specification: EMD file / empty entries
213                  * Unused entries in the EMD file are identified
214                  * by the name_len field equal to 0. However to
215                  * help future extension (or bug correction :-( ),
216                  * empty entries are filled with 0.
217                  */
218                 memset (&entry0, 0, sizeof (entry0));
219                 entry = &entry0;
220         } else if (entry->name_len > 0) {
221                 memset (entry->name + entry->name_len, '\0', 
222                         sizeof (entry->name) - entry->name_len);
223                 /* #Specification: EMD file / spare bytes
224                  * 10 bytes are unused in each record of the EMD. They
225                  * are set to 0 all the time, so it will be possible
226                  * to do new stuff and rely on the state of those
227                  * bytes in old EMD files.
228                  */
229                 memset (entry->spare, 0, sizeof (entry->spare));
230         }
231 
232         /* write the entry and update the parent timestamps */
233         mapping = emd_dentry->d_inode->i_mapping;
234         offs = info->f_pos & ~PAGE_CACHE_MASK;
235         ret = -ENOMEM;
236         page = grab_cache_page(mapping, info->f_pos>>PAGE_CACHE_SHIFT);
237         if (!page)
238                 goto out_dput;
239         p = (struct umsdos_dirent *) (page_address(page) + offs);
240         if (offs + info->recsize > PAGE_CACHE_SIZE) {
241                 ret = mapping->a_ops->prepare_write(NULL,page,offs,
242                                         PAGE_CACHE_SIZE);
243                 if (ret)
244                         goto out_unlock;
245                 page2 = grab_cache_page(mapping,
246                                         (info->f_pos>>PAGE_CACHE_SHIFT)+1);
247                 if (!page2)
248                         goto out_unlock2;
249                 ret = mapping->a_ops->prepare_write(NULL,page2,0,
250                                         offs+info->recsize-PAGE_CACHE_SIZE);
251                 if (ret)
252                         goto out_unlock3;
253                 p->name_len = entry->name_len;
254                 p->flags = entry->flags;
255                 p->nlink = cpu_to_le16(entry->nlink);
256                 p->uid = cpu_to_le16(entry->uid);
257                 p->gid = cpu_to_le16(entry->gid);
258                 p->atime = cpu_to_le32(entry->atime);
259                 p->mtime = cpu_to_le32(entry->mtime);
260                 p->ctime = cpu_to_le32(entry->ctime);
261                 p->rdev = cpu_to_le16(entry->rdev);
262                 p->mode = cpu_to_le16(entry->mode);
263                 memcpy(p->name,entry->name,
264                         (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare);
265                 memcpy(page_address(page2),
266                                 entry->spare+PAGE_CACHE_SIZE-offs,
267                                 offs+info->recsize-PAGE_CACHE_SIZE);
268                 ret = mapping->a_ops->commit_write(NULL,page2,0,
269                                         offs+info->recsize-PAGE_CACHE_SIZE);
270                 if (ret)
271                         goto out_unlock3;
272                 ret = mapping->a_ops->commit_write(NULL,page,offs,
273                                         PAGE_CACHE_SIZE);
274                 UnlockPage(page2);
275                 page_cache_release(page2);
276                 if (ret)
277                         goto out_unlock;
278         } else {
279                 ret = mapping->a_ops->prepare_write(NULL,page,offs,
280                                         offs + info->recsize);
281                 if (ret)
282                         goto out_unlock;
283                 p->name_len = entry->name_len;
284                 p->flags = entry->flags;
285                 p->nlink = cpu_to_le16(entry->nlink);
286                 p->uid = cpu_to_le16(entry->uid);
287                 p->gid = cpu_to_le16(entry->gid);
288                 p->atime = cpu_to_le32(entry->atime);
289                 p->mtime = cpu_to_le32(entry->mtime);
290                 p->ctime = cpu_to_le32(entry->ctime);
291                 p->rdev = cpu_to_le16(entry->rdev);
292                 p->mode = cpu_to_le16(entry->mode);
293                 memcpy(p->spare,entry->spare,((char*)p+info->recsize)-p->spare);
294                 ret = mapping->a_ops->commit_write(NULL,page,offs,
295                                         offs + info->recsize);
296                 if (ret)
297                         goto out_unlock;
298         }
299         UnlockPage(page);
300         page_cache_release(page);
301                 
302         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
303         mark_inode_dirty(dir);
304 
305 out_dput:
306         dput(emd_dentry);
307 out:
308         Printk (("umsdos_writeentry /mn/: returning %d...\n", ret));
309         return ret;
310 out_unlock3:
311         UnlockPage(page2);
312         page_cache_release(page2);
313 out_unlock2:
314         ClearPageUptodate(page);
315         kunmap(page);
316 out_unlock:
317         UnlockPage(page);
318         page_cache_release(page);
319         printk ("UMSDOS:  problem with EMD file:  can't write\n");
320         goto out_dput;
321 }
322 
323 /*
324  * General search, locate a name in the EMD file or an empty slot to
325  * store it. if info->entry.name_len == 0, search the first empty
326  * slot (of the proper size).
327  * 
328  * Return 0 if found, -ENOENT if not found, another error code if
329  * other problem.
330  * 
331  * So this routine is used to either find an existing entry or to
332  * create a new one, while making sure it is a new one. After you
333  * get -ENOENT, you make sure the entry is stuffed correctly and
334  * call umsdos_writeentry().
335  * 
336  * To delete an entry, you find it, zero out the entry (memset)
337  * and call umsdos_writeentry().
338  * 
339  * All this to say that umsdos_writeentry must be called after this
340  * function since it relies on the f_pos field of info.
341  *
342  * Note: the caller must hold a lock on the parent directory.
343  */
344 /* #Specification: EMD file structure
345  * The EMD file uses a fairly simple layout.  It is made of records
346  * (UMSDOS_REC_SIZE == 64).  When a name can't be written in a single
347  * record, multiple contiguous records are allocated.
348  */
349 
350 static int umsdos_find (struct dentry *demd, struct umsdos_info *info)
351 {
352         struct umsdos_dirent *entry = &info->entry;
353         int recsize = info->recsize;
354         struct inode *emd_dir;
355         int ret = -ENOENT;
356         struct {
357                 off_t posok;    /* Position available to store the entry */
358                 off_t one;      /* One empty position -> maybe <- large enough */
359         } empty;
360         int found = 0;
361         int empty_size = 0;
362         struct address_space *mapping;
363         filler_t *readpage;
364         struct page *page = NULL;
365         int index = -1;
366         int offs = PAGE_CACHE_SIZE,max_offs = PAGE_CACHE_SIZE;
367         char *p = NULL;
368         loff_t pos = 0;
369 
370         /* make sure there's an EMD file ... */
371         ret = -ENOENT;
372         emd_dir = demd->d_inode;
373         if (!emd_dir)
374                 goto out_dput;
375         mapping = emd_dir->i_mapping;
376         readpage = (filler_t*)mapping->a_ops->readpage;
377 
378         empty.posok = emd_dir->i_size;
379         while (1) {
380                 struct umsdos_dirent *rentry;
381                 int entry_size;
382 
383                 if (offs >= max_offs) {
384                         if (page) {
385                                 kunmap(page);
386                                 page_cache_release(page);
387                                 page = NULL;
388                         }
389                         if (pos >= emd_dir->i_size) {
390                                 info->f_pos = empty.posok;
391                                 break;
392                         }
393                         if (++index == (emd_dir->i_size>>PAGE_CACHE_SHIFT))
394                                 max_offs = emd_dir->i_size & ~PAGE_CACHE_MASK;
395                         offs -= PAGE_CACHE_SIZE;
396                         page = read_cache_page(mapping,index,readpage,NULL);
397                         if (IS_ERR(page))
398                                 goto sync_fail;
399                         wait_on_page(page);
400                         if (!Page_Uptodate(page))
401                                 goto async_fail;
402                         p = kmap(page);
403                 }
404 
405                 rentry = (struct umsdos_dirent *)(p+offs);
406 
407                 if (rentry->name_len == 0) {
408                         /* We are looking for an empty section at least */
409                         /* as large as recsize. */
410                         if (entry->name_len == 0) {
411                                 info->f_pos = pos;
412                                 ret = 0;
413                                 break;
414                         }
415                         offs += UMSDOS_REC_SIZE;
416                         pos += UMSDOS_REC_SIZE;
417                         if (found)
418                                 continue;
419                         if (!empty_size)
420                                 empty.one = pos-UMSDOS_REC_SIZE;
421                         empty_size += UMSDOS_REC_SIZE;
422                         if (empty_size == recsize) {
423                                 /* Here is a large enough section. */
424                                 empty.posok = empty.one;
425                                 found = 1;
426                         }
427                         continue;
428                 }
429 
430                 entry_size = umsdos_evalrecsize(rentry->name_len);
431                 if (entry_size > PAGE_CACHE_SIZE)
432                         goto async_fail;
433                 empty_size = 0;
434                 if (entry->name_len != rentry->name_len)
435                         goto skip_it;
436 
437                 if (entry_size + offs > PAGE_CACHE_SIZE) {
438                         /* Sucker spans the page boundary */
439                         int len = (p+PAGE_CACHE_SIZE)-rentry->name;
440                         struct page *next_page;
441                         char *q;
442                         next_page = read_cache_page(mapping,index+1,readpage,NULL);
443                         if (IS_ERR(next_page)) {
444                                 page_cache_release(page);
445                                 page = next_page;
446                                 goto sync_fail;
447                         }
448                         wait_on_page(next_page);
449                         if (!Page_Uptodate(next_page)) {
450                                 page_cache_release(page);
451                                 page = next_page;
452                                 goto async_fail;
453                         }
454                         q = kmap(next_page);
455                         if (memcmp(entry->name, rentry->name, len) ||
456                             memcmp(entry->name+len, q, entry->name_len-len)) {
457                                 kunmap(next_page);
458                                 page_cache_release(next_page);
459                                 goto skip_it;
460                         }
461                         kunmap(next_page);
462                         page_cache_release(next_page);
463                 } else if (memcmp (entry->name, rentry->name, entry->name_len))
464                         goto skip_it;
465 
466                 info->f_pos = pos;
467                 copy_entry(entry, rentry);
468                 ret = 0;
469                 break;
470 skip_it:
471                 offs+=entry_size;
472                 pos+=entry_size;
473         }
474         if (page) {
475                 kunmap(page);
476                 page_cache_release(page);
477         }
478         umsdos_manglename (info);
479 
480 out_dput:
481         dput(demd);
482         return ret;
483 
484 async_fail:
485         page_cache_release(page);
486         page = ERR_PTR(-EIO);
487 sync_fail:
488         return PTR_ERR(page);
489 }
490 
491 
492 /*
493  * Add a new entry in the EMD file.
494  * Return 0 if OK or a negative error code.
495  * Return -EEXIST if the entry already exists.
496  *
497  * Complete the information missing in info.
498  * 
499  * N.B. What if the EMD file doesn't exist?
500  */
501 
502 int umsdos_newentry (struct dentry *parent, struct umsdos_info *info)
503 {
504         int err, ret = -EEXIST;
505         struct dentry *demd = umsdos_get_emd_dentry(parent);
506 
507         ret = PTR_ERR(demd);
508         if (IS_ERR(demd))
509                 goto out;
510         err = umsdos_find (demd, info);
511         if (err && err == -ENOENT) {
512                 ret = umsdos_writeentry (parent, info, 0);
513                 Printk (("umsdos_writeentry EMD ret = %d\n", ret));
514         }
515 out:
516         return ret;
517 }
518 
519 
520 /*
521  * Create a new hidden link.
522  * Return 0 if OK, an error code if not.
523  */
524 
525 /* #Specification: hard link / hidden name
526  * When a hard link is created, the original file is renamed
527  * to a hidden name. The name is "..LINKNNN" where NNN is a
528  * number define from the entry offset in the EMD file.
529  */
530 int umsdos_newhidden (struct dentry *parent, struct umsdos_info *info)
531 {
532         int ret;
533         struct dentry *demd = umsdos_get_emd_dentry(parent);
534         ret = PTR_ERR(demd);
535         if (IS_ERR(demd))
536                 goto out;
537 
538         umsdos_parse ("..LINK", 6, info);
539         info->entry.name_len = 0;
540         ret = umsdos_find (demd, info);
541         if (ret == -ENOENT || ret == 0) {
542                 info->entry.name_len = sprintf (info->entry.name,
543                                                 "..LINK%ld", info->f_pos);
544                 ret = 0;
545         }
546 out:
547         return ret;
548 }
549 
550 
551 /*
552  * Remove an entry from the EMD file.
553  * Return 0 if OK, a negative error code otherwise.
554  * 
555  * Complete the information missing in info.
556  */
557 
558 int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir)
559 {
560         int ret;
561         struct dentry *demd = umsdos_get_emd_dentry(parent);
562 
563         ret = PTR_ERR(demd);
564         if (IS_ERR(demd))
565                 goto out;
566         ret = umsdos_find (demd, info);
567         if (ret)
568                 goto out;
569         if (info->entry.name_len == 0)
570                 goto out;
571 
572         if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) {
573                 if (S_ISDIR (info->entry.mode)) {
574                         ret = -EISDIR;
575                 } else {
576                         ret = -ENOTDIR;
577                 }
578                 goto out;
579         }
580         ret = umsdos_writeentry (parent, info, 1);
581 
582 out:
583         return ret;
584 }
585 
586 
587 /*
588  * Verify that an EMD directory is empty.
589  * Return: 
590  * 0 if not empty,
591  * 1 if empty (except for EMD file),
592  * 2 if empty or no EMD file.
593  */
594 
595 int umsdos_isempty (struct dentry *dentry)
596 {
597         struct dentry *demd;
598         int ret = 2;
599         loff_t pos = 0;
600 
601         demd = umsdos_get_emd_dentry(dentry);
602         if (IS_ERR(demd))
603                 goto out;
604         /* If the EMD file does not exist, it is certainly empty. :-) */
605         if (!demd->d_inode)
606                 goto out_dput;
607 
608         ret = 1;
609         while (pos < demd->d_inode->i_size) {
610                 struct umsdos_dirent entry;
611 
612                 if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0) {
613                         ret = 0;
614                         break;
615                 }
616                 if (entry.name_len != 0) {
617                         ret = 0;
618                         break;
619                 }
620         }
621 
622 out_dput:
623         dput(demd);
624 out:
625         return ret;
626 }
627 
628 /*
629  * Locate an entry in a EMD directory.
630  * Return 0 if OK, error code if not, generally -ENOENT.
631  *
632  * expect argument:
633  *      0: anything
634  *      1: file
635  *      2: directory
636  */
637 
638 int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
639                         int expect)
640 {               
641         int ret;
642         struct dentry *demd = umsdos_get_emd_dentry(parent);
643 
644         ret = PTR_ERR(demd);
645         if (IS_ERR(demd))
646                 goto out;
647         ret = umsdos_find (demd, info);
648         if (ret)
649                 goto out;
650 
651         switch (expect) {
652         case 1:
653                 if (S_ISDIR (info->entry.mode))
654                         ret = -EISDIR;
655                 break;
656         case 2:
657                 if (!S_ISDIR (info->entry.mode))
658                         ret = -ENOTDIR;
659         }
660 
661 out:
662         Printk (("umsdos_findentry: returning %d\n", ret));
663         return ret;
664 }
665 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.