OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [umsdos/] [emd.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.