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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [ext/] [namei.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1628 jcastillo
/*
2
 *  linux/fs/ext/namei.c
3
 *
4
 *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
5
 *
6
 *  from
7
 *
8
 *  linux/fs/minix/namei.c
9
 *
10
 *  Copyright (C) 1991, 1992  Linus Torvalds
11
 */
12
 
13
#include <linux/sched.h>
14
#include <linux/ext_fs.h>
15
#include <linux/kernel.h>
16
#include <linux/string.h>
17
#include <linux/stat.h>
18
#include <linux/fcntl.h>
19
#include <linux/errno.h>
20
 
21
#include <asm/segment.h>
22
 
23
/*
24
 * comment out this line if you want names > EXT_NAME_LEN chars to be
25
 * truncated. Else they will be disallowed.
26
 */
27
/* #define NO_TRUNCATE */
28
 
29
/*
30
 * EXT_DIR_PAD defines the directory entries boundaries
31
 *
32
 * NOTE: It must be a power of 2 and must be greater or equal than 8
33
 * because a directory entry needs 8 bytes for its fixed part
34
 * (4 bytes for the inode, 2 bytes for the entry length and 2 bytes
35
 * for the name length)
36
 */
37
#define EXT_DIR_PAD 8
38
 
39
/*
40
 *
41
 * EXT_DIR_MIN_SIZE is the minimal size of a directory entry
42
 *
43
 * During allocations, a directory entry is split into 2 ones
44
 * *ONLY* if the size of the unused part is greater than or
45
 * equal to EXT_DIR_MIN_SIZE
46
 */
47
#define EXT_DIR_MIN_SIZE 12
48
 
49
/*
50
 * ok, we cannot use strncmp, as the name is not in our data space.
51
 * Thus we'll have to use ext_match. No big problem. Match also makes
52
 * some sanity tests.
53
 *
54
 * NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure.
55
 */
56
static int ext_match(int len,const char * name,struct ext_dir_entry * de)
57
{
58
        if (!de || !de->inode || len > EXT_NAME_LEN)
59
                return 0;
60
        /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
61
        if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
62
                return 1;
63
        if (len != de->name_len)
64
                return 0;
65
        return !memcmp(name, de->name, len);
66
}
67
 
68
/*
69
 *      ext_find_entry()
70
 *
71
 * finds an entry in the specified directory with the wanted name. It
72
 * returns the cache buffer in which the entry was found, and the entry
73
 * itself (as a parameter - res_dir). It does NOT read the inode of the
74
 * entry - you'll have to do that yourself if you want to.
75
 *
76
 * addition for the ext file system : this function returns the previous
77
 * and next directory entries in the parameters prev_dir and next_dir
78
 */
79
static struct buffer_head * ext_find_entry(struct inode * dir,
80
        const char * name, int namelen, struct ext_dir_entry ** res_dir,
81
        struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir)
82
{
83
        long offset;
84
        struct buffer_head * bh;
85
        struct ext_dir_entry * de;
86
 
87
        *res_dir = NULL;
88
        if (!dir)
89
                return NULL;
90
#ifdef NO_TRUNCATE
91
        if (namelen > EXT_NAME_LEN)
92
                return NULL;
93
#else
94
        if (namelen > EXT_NAME_LEN)
95
                namelen = EXT_NAME_LEN;
96
#endif
97
        bh = ext_bread(dir,0,0);
98
        if (!bh)
99
                return NULL;
100
        if (prev_dir)
101
                *prev_dir = NULL;
102
        if (next_dir)
103
                *next_dir = NULL;
104
        offset = 0;
105
        de = (struct ext_dir_entry *) bh->b_data;
106
        while (offset < dir->i_size) {
107
                if ((char *)de >= BLOCK_SIZE+bh->b_data) {
108
                        brelse(bh);
109
                        bh = NULL;
110
                        bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
111
                        if (!bh)
112
                                continue;
113
                        de = (struct ext_dir_entry *) bh->b_data;
114
                        if (prev_dir)
115
                                *prev_dir = NULL;
116
                }
117
                if (de->rec_len < 8 || de->rec_len % 8 != 0 ||
118
                    de->rec_len < de->name_len + 8 ||
119
                    (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
120
                        printk ("ext_find_entry: bad dir entry\n");
121
                        printk ("dev=%s, dir=%ld, offset=%ld, "
122
                                "rec_len=%d, name_len=%d\n",
123
                                kdevname(dir->i_dev), dir->i_ino, offset,
124
                                de->rec_len, de->name_len);
125
                        de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE);
126
                        offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
127
                        continue;
128
/*                      brelse (bh);
129
                        return NULL; */
130
                }
131
                if (ext_match(namelen,name,de)) {
132
                        *res_dir = de;
133
                        if (next_dir)
134
                                if (offset + de->rec_len < dir->i_size &&
135
                                    ((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data)
136
                                        *next_dir = (struct ext_dir_entry *)
137
                                                ((char *) de + de->rec_len);
138
                                else
139
                                        *next_dir = NULL;
140
                        return bh;
141
                }
142
                offset += de->rec_len;
143
                if (prev_dir)
144
                        *prev_dir = de;
145
                de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
146
        }
147
        brelse(bh);
148
        return NULL;
149
}
150
 
151
int ext_lookup(struct inode * dir,const char * name, int len,
152
        struct inode ** result)
153
{
154
        int ino;
155
        struct ext_dir_entry * de;
156
        struct buffer_head * bh;
157
 
158
        *result = NULL;
159
        if (!dir)
160
                return -ENOENT;
161
        if (!S_ISDIR(dir->i_mode)) {
162
                iput(dir);
163
                return -ENOENT;
164
        }
165
        if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) {
166
                iput(dir);
167
                return -ENOENT;
168
        }
169
        ino = de->inode;
170
        brelse(bh);
171
        if (!(*result = iget(dir->i_sb,ino))) {
172
                iput(dir);
173
                return -EACCES;
174
        }
175
        iput(dir);
176
        return 0;
177
}
178
 
179
/*
180
 *      ext_add_entry()
181
 *
182
 * adds a file entry to the specified directory, using the same
183
 * semantics as ext_find_entry(). It returns NULL if it failed.
184
 *
185
 * NOTE!! The inode part of 'de' is left at 0 - which means you
186
 * may not sleep between calling this and putting something into
187
 * the entry, as someone else might have used it while you slept.
188
 */
189
static struct buffer_head * ext_add_entry(struct inode * dir,
190
        const char * name, int namelen, struct ext_dir_entry ** res_dir)
191
{
192
        int i;
193
        long offset;
194
        unsigned short rec_len;
195
        struct buffer_head * bh;
196
        struct ext_dir_entry * de, * de1;
197
 
198
        *res_dir = NULL;
199
        if (!dir)
200
                return NULL;
201
#ifdef NO_TRUNCATE
202
        if (namelen > EXT_NAME_LEN)
203
                return NULL;
204
#else
205
        if (namelen > EXT_NAME_LEN)
206
                namelen = EXT_NAME_LEN;
207
#endif
208
        if (!namelen)
209
                return NULL;
210
        bh = ext_bread(dir,0,0);
211
        if (!bh)
212
                return NULL;
213
        rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD;
214
        offset = 0;
215
        de = (struct ext_dir_entry *) bh->b_data;
216
        while (1) {
217
                if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) {
218
#ifdef EXTFS_DEBUG
219
printk ("ext_add_entry: skipping to next block\n");
220
#endif
221
                        brelse(bh);
222
                        bh = NULL;
223
                        bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
224
                        if (!bh)
225
                                return NULL;
226
                        de = (struct ext_dir_entry *) bh->b_data;
227
                }
228
                if (offset >= dir->i_size) {
229
                        /* Check that the directory entry fits in the block */
230
                        if (offset % BLOCK_SIZE == 0  ||
231
                            (BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) {
232
                                if ((offset % BLOCK_SIZE) != 0) {
233
                                        /* If the entry does not fit in the
234
                                           block, the remainder of the block
235
                                           becomes an unused entry */
236
                                        de->inode = 0;
237
                                        de->rec_len = BLOCK_SIZE
238
                                                - (offset & (BLOCK_SIZE - 1));
239
                                        de->name_len = 0;
240
                                        offset += de->rec_len;
241
                                        dir->i_size += de->rec_len;
242
                                        dir->i_dirt = 1;
243
#if 0
244
                                        dir->i_ctime = CURRENT_TIME;
245
#endif
246
                                        mark_buffer_dirty(bh, 1);
247
                                }
248
                                brelse (bh);
249
                                bh = NULL;
250
#ifdef EXTFS_DEBUG
251
printk ("ext_add_entry : creating next block\n");
252
#endif
253
                                bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,1);
254
                                if (!bh)
255
                                        return NULL; /* Other thing to do ??? */
256
                                de = (struct ext_dir_entry *) bh->b_data;
257
                        }
258
                        /* Allocate the entry */
259
                        de->inode=0;
260
                        de->rec_len = rec_len;
261
                        dir->i_size += de->rec_len;
262
                        dir->i_dirt = 1;
263
#if 0
264
                        dir->i_ctime = CURRENT_TIME;
265
#endif
266
                }
267
                if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
268
                    de->rec_len < de->name_len + 8 ||
269
                    (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
270
                        printk ("ext_addr_entry: bad dir entry\n");
271
                        printk ("dev=%s, dir=%ld, offset=%ld, "
272
                                "rec_len=%d, name_len=%d\n",
273
                                kdevname(dir->i_dev), dir->i_ino, offset,
274
                                de->rec_len, de->name_len);
275
                        brelse (bh);
276
                        return NULL;
277
                }
278
                if (!de->inode && de->rec_len >= rec_len) {
279
                        if (de->rec_len > rec_len
280
                            && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) {
281
                                /* The found entry is too big : it is split
282
                                   into 2 ones :
283
                                   - the 1st one will be used to hold the name,
284
                                   - the 2nd one is unused */
285
                                de1 = (struct ext_dir_entry *) ((char *) de + rec_len);
286
                                de1->inode = 0;
287
                                de1->rec_len = de->rec_len - rec_len;
288
                                de1->name_len = 0;
289
                                de->rec_len = rec_len;
290
                        }
291
                        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
292
                        dir->i_dirt = 1;
293
                        de->name_len = namelen;
294
                        for (i=0; i < namelen ; i++)
295
                                de->name[i] = name[i];
296
                        mark_buffer_dirty(bh, 1);
297
                        *res_dir = de;
298
                        return bh;
299
                }
300
                offset += de->rec_len;
301
                de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
302
        }
303
        brelse(bh);
304
        return NULL;
305
}
306
 
307
int ext_create(struct inode * dir,const char * name, int len, int mode,
308
        struct inode ** result)
309
{
310
        struct inode * inode;
311
        struct buffer_head * bh;
312
        struct ext_dir_entry * de;
313
 
314
        *result = NULL;
315
        if (!dir)
316
                return -ENOENT;
317
        inode = ext_new_inode(dir);
318
        if (!inode) {
319
                iput(dir);
320
                return -ENOSPC;
321
        }
322
        inode->i_op = &ext_file_inode_operations;
323
        inode->i_mode = mode;
324
        inode->i_dirt = 1;
325
        bh = ext_add_entry(dir,name,len,&de);
326
        if (!bh) {
327
                inode->i_nlink--;
328
                inode->i_dirt = 1;
329
                iput(inode);
330
                iput(dir);
331
                return -ENOSPC;
332
        }
333
        de->inode = inode->i_ino;
334
        mark_buffer_dirty(bh, 1);
335
        brelse(bh);
336
        iput(dir);
337
        *result = inode;
338
        return 0;
339
}
340
 
341
int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
342
{
343
        struct inode * inode;
344
        struct buffer_head * bh;
345
        struct ext_dir_entry * de;
346
 
347
        if (!dir)
348
                return -ENOENT;
349
        bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
350
        if (bh) {
351
                brelse(bh);
352
                iput(dir);
353
                return -EEXIST;
354
        }
355
        inode = ext_new_inode(dir);
356
        if (!inode) {
357
                iput(dir);
358
                return -ENOSPC;
359
        }
360
        inode->i_uid = current->fsuid;
361
        inode->i_mode = mode;
362
        inode->i_op = NULL;
363
        if (S_ISREG(inode->i_mode))
364
                inode->i_op = &ext_file_inode_operations;
365
        else if (S_ISDIR(inode->i_mode)) {
366
                inode->i_op = &ext_dir_inode_operations;
367
                if (dir->i_mode & S_ISGID)
368
                        inode->i_mode |= S_ISGID;
369
        }
370
        else if (S_ISLNK(inode->i_mode))
371
                inode->i_op = &ext_symlink_inode_operations;
372
        else if (S_ISCHR(inode->i_mode))
373
                inode->i_op = &chrdev_inode_operations;
374
        else if (S_ISBLK(inode->i_mode))
375
                inode->i_op = &blkdev_inode_operations;
376
        else if (S_ISFIFO(inode->i_mode))
377
                init_fifo(inode);
378
        if (S_ISBLK(mode) || S_ISCHR(mode))
379
                inode->i_rdev = to_kdev_t(rdev);
380
#if 0
381
        inode->i_mtime = inode->i_atime = CURRENT_TIME;
382
#endif
383
        inode->i_dirt = 1;
384
        bh = ext_add_entry(dir,name,len,&de);
385
        if (!bh) {
386
                inode->i_nlink--;
387
                inode->i_dirt = 1;
388
                iput(inode);
389
                iput(dir);
390
                return -ENOSPC;
391
        }
392
        de->inode = inode->i_ino;
393
        mark_buffer_dirty(bh, 1);
394
        brelse(bh);
395
        iput(dir);
396
        iput(inode);
397
        return 0;
398
}
399
 
400
int ext_mkdir(struct inode * dir, const char * name, int len, int mode)
401
{
402
        struct inode * inode;
403
        struct buffer_head * bh, *dir_block;
404
        struct ext_dir_entry * de;
405
 
406
        bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
407
        if (bh) {
408
                brelse(bh);
409
                iput(dir);
410
                return -EEXIST;
411
        }
412
        inode = ext_new_inode(dir);
413
        if (!inode) {
414
                iput(dir);
415
                return -ENOSPC;
416
        }
417
        inode->i_op = &ext_dir_inode_operations;
418
        inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".."
419
                                        - 4 bytes for the inode number,
420
                                        - 2 bytes for the record length
421
                                        - 2 bytes for the name length
422
                                        - 8 bytes for the name */
423
#if 0
424
        inode->i_mtime = inode->i_atime = CURRENT_TIME;
425
#endif
426
        dir_block = ext_bread(inode,0,1);
427
        if (!dir_block) {
428
                iput(dir);
429
                inode->i_nlink--;
430
                inode->i_dirt = 1;
431
                iput(inode);
432
                return -ENOSPC;
433
        }
434
        de = (struct ext_dir_entry *) dir_block->b_data;
435
        de->inode=inode->i_ino;
436
        de->rec_len=16;
437
        de->name_len=1;
438
        strcpy(de->name,".");
439
        de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
440
        de->inode = dir->i_ino;
441
        de->rec_len=16;
442
        de->name_len=2;
443
        strcpy(de->name,"..");
444
        inode->i_nlink = 2;
445
        mark_buffer_dirty(dir_block, 1);
446
        brelse(dir_block);
447
        inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
448
        if (dir->i_mode & S_ISGID)
449
                inode->i_mode |= S_ISGID;
450
        inode->i_dirt = 1;
451
        bh = ext_add_entry(dir,name,len,&de);
452
        if (!bh) {
453
                iput(dir);
454
                inode->i_nlink=0;
455
                iput(inode);
456
                return -ENOSPC;
457
        }
458
        de->inode = inode->i_ino;
459
        mark_buffer_dirty(bh, 1);
460
        dir->i_nlink++;
461
        dir->i_dirt = 1;
462
        iput(dir);
463
        iput(inode);
464
        brelse(bh);
465
        return 0;
466
}
467
 
468
/*
469
 * routine to check that the specified directory is empty (for rmdir)
470
 */
471
static int empty_dir(struct inode * inode)
472
{
473
        unsigned long offset;
474
        struct buffer_head * bh;
475
        struct ext_dir_entry * de, * de1;
476
 
477
        if (inode->i_size < 2 * 12 || !(bh = ext_bread(inode,0,0))) {
478
                printk("warning - bad directory on dev %s\n",
479
                       kdevname(inode->i_dev));
480
                return 1;
481
        }
482
        de = (struct ext_dir_entry *) bh->b_data;
483
        de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len);
484
        if (de->inode != inode->i_ino || !de1->inode ||
485
            strcmp(".",de->name) || strcmp("..",de1->name)) {
486
                printk("warning - bad directory on dev %s\n",
487
                       kdevname(inode->i_dev));
488
                return 1;
489
        }
490
        offset = de->rec_len + de1->rec_len;
491
        de = (struct ext_dir_entry *) ((char *) de1 + de1->rec_len);
492
        while (offset < inode->i_size ) {
493
                if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
494
                        brelse(bh);
495
                        bh = ext_bread(inode, offset >> BLOCK_SIZE_BITS,1);
496
                        if (!bh) {
497
                                offset += BLOCK_SIZE;
498
                                continue;
499
                        }
500
                        de = (struct ext_dir_entry *) bh->b_data;
501
                }
502
                if (de->rec_len < 8 || de->rec_len %4 != 0 ||
503
                    de->rec_len < de->name_len + 8) {
504
                        printk ("empty_dir: bad dir entry\n");
505
                        printk ("dev=%s, dir=%ld, offset=%ld, "
506
                                "rec_len=%d, name_len=%d\n",
507
                                kdevname(inode->i_dev), inode->i_ino,
508
                                offset, de->rec_len, de->name_len);
509
                        brelse (bh);
510
                        return 1;
511
                }
512
                if (de->inode) {
513
                        brelse(bh);
514
                        return 0;
515
                }
516
                offset += de->rec_len;
517
                de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
518
        }
519
        brelse(bh);
520
        return 1;
521
}
522
 
523
static inline void ext_merge_entries (struct ext_dir_entry * de,
524
        struct ext_dir_entry * pde, struct ext_dir_entry * nde)
525
{
526
        if (nde && !nde->inode)
527
                de->rec_len += nde->rec_len;
528
        if (pde && !pde->inode)
529
                pde->rec_len += de->rec_len;
530
}
531
 
532
int ext_rmdir(struct inode * dir, const char * name, int len)
533
{
534
        int retval;
535
        struct inode * inode;
536
        struct buffer_head * bh;
537
        struct ext_dir_entry * de, * pde, * nde;
538
 
539
        inode = NULL;
540
        bh = ext_find_entry(dir,name,len,&de,&pde,&nde);
541
        retval = -ENOENT;
542
        if (!bh)
543
                goto end_rmdir;
544
        retval = -EPERM;
545
        if (!(inode = iget(dir->i_sb, de->inode)))
546
                goto end_rmdir;
547
        if ((dir->i_mode & S_ISVTX) && !fsuser() &&
548
            current->fsuid != inode->i_uid &&
549
            current->fsuid != dir->i_uid)
550
                goto end_rmdir;
551
        if (inode->i_dev != dir->i_dev)
552
                goto end_rmdir;
553
        if (inode == dir)       /* we may not delete ".", but "../dir" is ok */
554
                goto end_rmdir;
555
        if (!S_ISDIR(inode->i_mode)) {
556
                retval = -ENOTDIR;
557
                goto end_rmdir;
558
        }
559
        if (!empty_dir(inode)) {
560
                retval = -ENOTEMPTY;
561
                goto end_rmdir;
562
        }
563
        if (inode->i_count > 1) {
564
                retval = -EBUSY;
565
                goto end_rmdir;
566
        }
567
        if (inode->i_nlink != 2)
568
                printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
569
        de->inode = 0;
570
        de->name_len = 0;
571
        ext_merge_entries (de, pde, nde);
572
        mark_buffer_dirty(bh, 1);
573
        inode->i_nlink=0;
574
        inode->i_dirt=1;
575
        dir->i_nlink--;
576
        inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
577
        dir->i_dirt=1;
578
        retval = 0;
579
end_rmdir:
580
        iput(dir);
581
        iput(inode);
582
        brelse(bh);
583
        return retval;
584
}
585
 
586
int ext_unlink(struct inode * dir, const char * name, int len)
587
{
588
        int retval;
589
        struct inode * inode;
590
        struct buffer_head * bh;
591
        struct ext_dir_entry * de, * pde, * nde;
592
 
593
        retval = -ENOENT;
594
        inode = NULL;
595
        bh = ext_find_entry(dir,name,len,&de,&pde,&nde);
596
        if (!bh)
597
                goto end_unlink;
598
        if (!(inode = iget(dir->i_sb, de->inode)))
599
                goto end_unlink;
600
        retval = -EPERM;
601
        if ((dir->i_mode & S_ISVTX) && !fsuser() &&
602
            current->fsuid != inode->i_uid &&
603
            current->fsuid != dir->i_uid)
604
                goto end_unlink;
605
        if (S_ISDIR(inode->i_mode))
606
                goto end_unlink;
607
        if (!inode->i_nlink) {
608
                printk("Deleting nonexistent file (%s:%ld), %d\n",
609
                       kdevname(inode->i_dev), inode->i_ino,
610
                       inode->i_nlink);
611
                inode->i_nlink=1;
612
        }
613
        de->inode = 0;
614
        de->name_len = 0;
615
        ext_merge_entries (de, pde, nde);
616
        mark_buffer_dirty(bh, 1);
617
        inode->i_nlink--;
618
        inode->i_dirt = 1;
619
        inode->i_ctime = CURRENT_TIME;
620
        dir->i_ctime = dir->i_mtime = inode->i_ctime;
621
        dir->i_dirt = 1;
622
        retval = 0;
623
end_unlink:
624
        brelse(bh);
625
        iput(inode);
626
        iput(dir);
627
        return retval;
628
}
629
 
630
int ext_symlink(struct inode * dir, const char * name, int len, const char * symname)
631
{
632
        struct ext_dir_entry * de;
633
        struct inode * inode = NULL;
634
        struct buffer_head * bh = NULL, * name_block = NULL;
635
        int i;
636
        char c;
637
 
638
        if (!(inode = ext_new_inode(dir))) {
639
                iput(dir);
640
                return -ENOSPC;
641
        }
642
        inode->i_mode = S_IFLNK | 0777;
643
        inode->i_op = &ext_symlink_inode_operations;
644
        name_block = ext_bread(inode,0,1);
645
        if (!name_block) {
646
                iput(dir);
647
                inode->i_nlink--;
648
                inode->i_dirt = 1;
649
                iput(inode);
650
                return -ENOSPC;
651
        }
652
        i = 0;
653
        while (i < 1023 && (c = *(symname++)))
654
                name_block->b_data[i++] = c;
655
        name_block->b_data[i] = 0;
656
        mark_buffer_dirty(name_block, 1);
657
        brelse(name_block);
658
        inode->i_size = i;
659
        inode->i_dirt = 1;
660
        bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
661
        if (bh) {
662
                inode->i_nlink--;
663
                inode->i_dirt = 1;
664
                iput(inode);
665
                brelse(bh);
666
                iput(dir);
667
                return -EEXIST;
668
        }
669
        bh = ext_add_entry(dir,name,len,&de);
670
        if (!bh) {
671
                inode->i_nlink--;
672
                inode->i_dirt = 1;
673
                iput(inode);
674
                iput(dir);
675
                return -ENOSPC;
676
        }
677
        de->inode = inode->i_ino;
678
        mark_buffer_dirty(bh, 1);
679
        brelse(bh);
680
        iput(dir);
681
        iput(inode);
682
        return 0;
683
}
684
 
685
int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
686
{
687
        struct ext_dir_entry * de;
688
        struct buffer_head * bh;
689
 
690
        if (S_ISDIR(oldinode->i_mode)) {
691
                iput(oldinode);
692
                iput(dir);
693
                return -EPERM;
694
        }
695
        if (oldinode->i_nlink > 32000) {
696
                iput(oldinode);
697
                iput(dir);
698
                return -EMLINK;
699
        }
700
        bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
701
        if (bh) {
702
                brelse(bh);
703
                iput(dir);
704
                iput(oldinode);
705
                return -EEXIST;
706
        }
707
        bh = ext_add_entry(dir,name,len,&de);
708
        if (!bh) {
709
                iput(dir);
710
                iput(oldinode);
711
                return -ENOSPC;
712
        }
713
        de->inode = oldinode->i_ino;
714
        mark_buffer_dirty(bh, 1);
715
        brelse(bh);
716
        iput(dir);
717
        oldinode->i_nlink++;
718
        oldinode->i_ctime = CURRENT_TIME;
719
        oldinode->i_dirt = 1;
720
        iput(oldinode);
721
        return 0;
722
}
723
 
724
static int subdir(struct inode * new_inode, struct inode * old_inode)
725
{
726
        int ino;
727
        int result;
728
 
729
        new_inode->i_count++;
730
        result = 0;
731
        for (;;) {
732
                if (new_inode == old_inode) {
733
                        result = 1;
734
                        break;
735
                }
736
                if (new_inode->i_dev != old_inode->i_dev)
737
                        break;
738
                ino = new_inode->i_ino;
739
                if (ext_lookup(new_inode,"..",2,&new_inode))
740
                        break;
741
                if (new_inode->i_ino == ino)
742
                        break;
743
        }
744
        iput(new_inode);
745
        return result;
746
}
747
 
748
#define PARENT_INO(buffer) \
749
((struct ext_dir_entry *) ((char *) buffer + \
750
((struct ext_dir_entry *) buffer)->rec_len))->inode
751
 
752
#define PARENT_NAME(buffer) \
753
((struct ext_dir_entry *) ((char *) buffer + \
754
((struct ext_dir_entry *) buffer)->rec_len))->name
755
 
756
/*
757
 * rename uses retrying to avoid race-conditions: at least they should be minimal.
758
 * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
759
 * checks fail, it tries to restart itself again. Very practical - no changes
760
 * are done until we know everything works ok.. and then all the changes can be
761
 * done in one fell swoop when we have claimed all the buffers needed.
762
 *
763
 * Anybody can rename anything with this: the permission checks are left to the
764
 * higher-level routines.
765
 */
766
static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_len,
767
        struct inode * new_dir, const char * new_name, int new_len)
768
{
769
        struct inode * old_inode, * new_inode;
770
        struct buffer_head * old_bh, * new_bh, * dir_bh;
771
        struct ext_dir_entry * old_de, * new_de, * pde, * nde;
772
        int retval;
773
 
774
        goto start_up;
775
try_again:
776
        brelse(old_bh);
777
        brelse(new_bh);
778
        brelse(dir_bh);
779
        iput(old_inode);
780
        iput(new_inode);
781
        current->counter = 0;
782
        schedule();
783
start_up:
784
        old_inode = new_inode = NULL;
785
        old_bh = new_bh = dir_bh = NULL;
786
        old_bh = ext_find_entry(old_dir,old_name,old_len,&old_de,&pde,&nde);
787
        retval = -ENOENT;
788
        if (!old_bh)
789
                goto end_rename;
790
        old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */
791
        if (!old_inode)
792
                goto end_rename;
793
        retval = -EPERM;
794
        if ((old_dir->i_mode & S_ISVTX) &&
795
            current->fsuid != old_inode->i_uid &&
796
            current->fsuid != old_dir->i_uid && !fsuser())
797
                goto end_rename;
798
        new_bh = ext_find_entry(new_dir,new_name,new_len,&new_de,NULL,NULL);
799
        if (new_bh) {
800
                new_inode = __iget(new_dir->i_sb, new_de->inode,0); /* don't cross mnt-points */
801
                if (!new_inode) {
802
                        brelse(new_bh);
803
                        new_bh = NULL;
804
                }
805
        }
806
        if (new_inode == old_inode) {
807
                retval = 0;
808
                goto end_rename;
809
        }
810
        if (new_inode && S_ISDIR(new_inode->i_mode)) {
811
                retval = -EEXIST;
812
                goto end_rename;
813
        }
814
        retval = -EPERM;
815
        if (new_inode && (new_dir->i_mode & S_ISVTX) &&
816
            current->fsuid != new_inode->i_uid &&
817
            current->fsuid != new_dir->i_uid && !fsuser())
818
                goto end_rename;
819
        if (S_ISDIR(old_inode->i_mode)) {
820
                retval = -EEXIST;
821
                if (new_bh)
822
                        goto end_rename;
823
                if ((retval = permission(old_inode, MAY_WRITE)) != 0)
824
                        goto end_rename;
825
                retval = -EINVAL;
826
                if (subdir(new_dir, old_inode))
827
                        goto end_rename;
828
                retval = -EIO;
829
                dir_bh = ext_bread(old_inode,0,0);
830
                if (!dir_bh)
831
                        goto end_rename;
832
                if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
833
                        goto end_rename;
834
        }
835
        if (!new_bh)
836
                new_bh = ext_add_entry(new_dir,new_name,new_len,&new_de);
837
        retval = -ENOSPC;
838
        if (!new_bh)
839
                goto end_rename;
840
/* sanity checking before doing the rename - avoid races */
841
        if (new_inode && (new_de->inode != new_inode->i_ino))
842
                goto try_again;
843
        if (new_de->inode && !new_inode)
844
                goto try_again;
845
        if (old_de->inode != old_inode->i_ino)
846
                goto try_again;
847
/* ok, that's it */
848
        old_de->inode = 0;
849
        old_de->name_len = 0;
850
        new_de->inode = old_inode->i_ino;
851
        ext_merge_entries (old_de, pde, nde);
852
        if (new_inode) {
853
                new_inode->i_nlink--;
854
                new_inode->i_dirt = 1;
855
        }
856
        mark_buffer_dirty(old_bh, 1);
857
        mark_buffer_dirty(new_bh, 1);
858
        if (dir_bh) {
859
                PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
860
                mark_buffer_dirty(dir_bh, 1);
861
                old_dir->i_nlink--;
862
                new_dir->i_nlink++;
863
                old_dir->i_dirt = 1;
864
                new_dir->i_dirt = 1;
865
        }
866
        retval = 0;
867
end_rename:
868
        brelse(dir_bh);
869
        brelse(old_bh);
870
        brelse(new_bh);
871
        iput(old_inode);
872
        iput(new_inode);
873
        iput(old_dir);
874
        iput(new_dir);
875
        return retval;
876
}
877
 
878
/*
879
 * Ok, rename also locks out other renames, as they can change the parent of
880
 * a directory, and we don't want any races. Other races are checked for by
881
 * "do_rename()", which restarts if there are inconsistencies.
882
 *
883
 * Note that there is no race between different filesystems: it's only within
884
 * the same device that races occur: many renames can happen at once, as long
885
 * as they are on different partitions.
886
 */
887
int ext_rename(struct inode * old_dir, const char * old_name, int old_len,
888
        struct inode * new_dir, const char * new_name, int new_len,
889
        int must_be_dir)
890
{
891
        static struct wait_queue * wait = NULL;
892
        static int lock = 0;
893
        int result;
894
 
895
        while (lock)
896
                sleep_on(&wait);
897
        lock = 1;
898
        result = do_ext_rename(old_dir, old_name, old_len,
899
                new_dir, new_name, new_len);
900
        lock = 0;
901
        wake_up(&wait);
902
        return result;
903
}

powered by: WebSVN 2.1.0

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