diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/dir.c | 86 |
1 files changed, 68 insertions, 18 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index affd3ae52e55..b483e5d206cb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/nfs_mount.h> | 30 | #include <linux/nfs_mount.h> |
31 | #include <linux/pagemap.h> | 31 | #include <linux/pagemap.h> |
32 | #include <linux/smp_lock.h> | 32 | #include <linux/smp_lock.h> |
33 | #include <linux/pagevec.h> | ||
33 | #include <linux/namei.h> | 34 | #include <linux/namei.h> |
34 | #include <linux/mount.h> | 35 | #include <linux/mount.h> |
35 | 36 | ||
@@ -1441,39 +1442,88 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) | |||
1441 | return error; | 1442 | return error; |
1442 | } | 1443 | } |
1443 | 1444 | ||
1444 | static int | 1445 | /* |
1445 | nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | 1446 | * To create a symbolic link, most file systems instantiate a new inode, |
1447 | * add a page to it containing the path, then write it out to the disk | ||
1448 | * using prepare_write/commit_write. | ||
1449 | * | ||
1450 | * Unfortunately the NFS client can't create the in-core inode first | ||
1451 | * because it needs a file handle to create an in-core inode (see | ||
1452 | * fs/nfs/inode.c:nfs_fhget). We only have a file handle *after* the | ||
1453 | * symlink request has completed on the server. | ||
1454 | * | ||
1455 | * So instead we allocate a raw page, copy the symname into it, then do | ||
1456 | * the SYMLINK request with the page as the buffer. If it succeeds, we | ||
1457 | * now have a new file handle and can instantiate an in-core NFS inode | ||
1458 | * and move the raw page into its mapping. | ||
1459 | */ | ||
1460 | static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | ||
1446 | { | 1461 | { |
1462 | struct pagevec lru_pvec; | ||
1463 | struct page *page; | ||
1464 | char *kaddr; | ||
1447 | struct iattr attr; | 1465 | struct iattr attr; |
1448 | struct qstr qsymname; | 1466 | unsigned int pathlen = strlen(symname); |
1467 | struct qstr qsymname = { | ||
1468 | .name = symname, | ||
1469 | .len = pathlen, | ||
1470 | }; | ||
1449 | int error; | 1471 | int error; |
1450 | 1472 | ||
1451 | dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, | 1473 | dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, |
1452 | dir->i_ino, dentry->d_name.name, symname); | 1474 | dir->i_ino, dentry->d_name.name, symname); |
1453 | 1475 | ||
1454 | #ifdef NFS_PARANOIA | 1476 | if (pathlen > PAGE_SIZE) |
1455 | if (dentry->d_inode) | 1477 | return -ENAMETOOLONG; |
1456 | printk("nfs_proc_symlink: %s/%s not negative!\n", | ||
1457 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
1458 | #endif | ||
1459 | /* | ||
1460 | * Fill in the sattr for the call. | ||
1461 | * Note: SunOS 4.1.2 crashes if the mode isn't initialized! | ||
1462 | */ | ||
1463 | attr.ia_valid = ATTR_MODE; | ||
1464 | attr.ia_mode = S_IFLNK | S_IRWXUGO; | ||
1465 | 1478 | ||
1466 | qsymname.name = symname; | 1479 | attr.ia_mode = S_IFLNK | S_IRWXUGO; |
1467 | qsymname.len = strlen(symname); | 1480 | attr.ia_valid = ATTR_MODE; |
1468 | 1481 | ||
1469 | lock_kernel(); | 1482 | lock_kernel(); |
1483 | |||
1484 | page = alloc_page(GFP_KERNEL); | ||
1485 | if (!page) { | ||
1486 | unlock_kernel(); | ||
1487 | return -ENOMEM; | ||
1488 | } | ||
1489 | |||
1490 | kaddr = kmap_atomic(page, KM_USER0); | ||
1491 | memcpy(kaddr, symname, pathlen); | ||
1492 | if (pathlen < PAGE_SIZE) | ||
1493 | memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen); | ||
1494 | kunmap_atomic(kaddr, KM_USER0); | ||
1495 | |||
1496 | /* XXX: eventually this will pass in {page, pathlen}, | ||
1497 | * instead of qsymname; need XDR changes for that */ | ||
1470 | nfs_begin_data_update(dir); | 1498 | nfs_begin_data_update(dir); |
1471 | error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr); | 1499 | error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr); |
1472 | nfs_end_data_update(dir); | 1500 | nfs_end_data_update(dir); |
1473 | if (!error) | 1501 | if (error != 0) { |
1502 | dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n", | ||
1503 | dir->i_sb->s_id, dir->i_ino, | ||
1504 | dentry->d_name.name, symname, error); | ||
1474 | d_drop(dentry); | 1505 | d_drop(dentry); |
1506 | __free_page(page); | ||
1507 | unlock_kernel(); | ||
1508 | return error; | ||
1509 | } | ||
1510 | |||
1511 | /* | ||
1512 | * No big deal if we can't add this page to the page cache here. | ||
1513 | * READLINK will get the missing page from the server if needed. | ||
1514 | */ | ||
1515 | pagevec_init(&lru_pvec, 0); | ||
1516 | if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0, | ||
1517 | GFP_KERNEL)) { | ||
1518 | if (!pagevec_add(&lru_pvec, page)) | ||
1519 | __pagevec_lru_add(&lru_pvec); | ||
1520 | SetPageUptodate(page); | ||
1521 | unlock_page(page); | ||
1522 | } else | ||
1523 | __free_page(page); | ||
1524 | |||
1475 | unlock_kernel(); | 1525 | unlock_kernel(); |
1476 | return error; | 1526 | return 0; |
1477 | } | 1527 | } |
1478 | 1528 | ||
1479 | static int | 1529 | static int |