aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/dir.c86
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
1444static int 1445/*
1445nfs_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 */
1460static 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)
1455if (dentry->d_inode) 1477 return -ENAMETOOLONG;
1456printk("nfs_proc_symlink: %s/%s not negative!\n",
1457dentry->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
1479static int 1529static int