|
@@ -17,6 +17,7 @@
|
|
#include <linux/syscalls.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/string.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/mm.h>
|
|
|
|
+#include <linux/fdtable.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
@@ -106,9 +107,10 @@ static void dentry_lru_remove(struct dentry *dentry)
|
|
/*
|
|
/*
|
|
* Release the dentry's inode, using the filesystem
|
|
* Release the dentry's inode, using the filesystem
|
|
* d_iput() operation if defined.
|
|
* d_iput() operation if defined.
|
|
- * Called with dcache_lock and per dentry lock held, drops both.
|
|
|
|
*/
|
|
*/
|
|
static void dentry_iput(struct dentry * dentry)
|
|
static void dentry_iput(struct dentry * dentry)
|
|
|
|
+ __releases(dentry->d_lock)
|
|
|
|
+ __releases(dcache_lock)
|
|
{
|
|
{
|
|
struct inode *inode = dentry->d_inode;
|
|
struct inode *inode = dentry->d_inode;
|
|
if (inode) {
|
|
if (inode) {
|
|
@@ -132,12 +134,13 @@ static void dentry_iput(struct dentry * dentry)
|
|
* d_kill - kill dentry and return parent
|
|
* d_kill - kill dentry and return parent
|
|
* @dentry: dentry to kill
|
|
* @dentry: dentry to kill
|
|
*
|
|
*
|
|
- * Called with dcache_lock and d_lock, releases both. The dentry must
|
|
|
|
- * already be unhashed and removed from the LRU.
|
|
|
|
|
|
+ * The dentry must already be unhashed and removed from the LRU.
|
|
*
|
|
*
|
|
* If this is the root of the dentry tree, return NULL.
|
|
* If this is the root of the dentry tree, return NULL.
|
|
*/
|
|
*/
|
|
static struct dentry *d_kill(struct dentry *dentry)
|
|
static struct dentry *d_kill(struct dentry *dentry)
|
|
|
|
+ __releases(dentry->d_lock)
|
|
|
|
+ __releases(dcache_lock)
|
|
{
|
|
{
|
|
struct dentry *parent;
|
|
struct dentry *parent;
|
|
|
|
|
|
@@ -383,11 +386,11 @@ restart:
|
|
* Try to prune ancestors as well. This is necessary to prevent
|
|
* Try to prune ancestors as well. This is necessary to prevent
|
|
* quadratic behavior of shrink_dcache_parent(), but is also expected
|
|
* quadratic behavior of shrink_dcache_parent(), but is also expected
|
|
* to be beneficial in reducing dentry cache fragmentation.
|
|
* to be beneficial in reducing dentry cache fragmentation.
|
|
- *
|
|
|
|
- * Called with dcache_lock, drops it and then regains.
|
|
|
|
- * Called with dentry->d_lock held, drops it.
|
|
|
|
*/
|
|
*/
|
|
static void prune_one_dentry(struct dentry * dentry)
|
|
static void prune_one_dentry(struct dentry * dentry)
|
|
|
|
+ __releases(dentry->d_lock)
|
|
|
|
+ __releases(dcache_lock)
|
|
|
|
+ __acquires(dcache_lock)
|
|
{
|
|
{
|
|
__d_drop(dentry);
|
|
__d_drop(dentry);
|
|
dentry = d_kill(dentry);
|
|
dentry = d_kill(dentry);
|
|
@@ -1604,10 +1607,9 @@ static int d_isparent(struct dentry *p1, struct dentry *p2)
|
|
*
|
|
*
|
|
* Note: If ever the locking in lock_rename() changes, then please
|
|
* Note: If ever the locking in lock_rename() changes, then please
|
|
* remember to update this too...
|
|
* remember to update this too...
|
|
- *
|
|
|
|
- * On return, dcache_lock will have been unlocked.
|
|
|
|
*/
|
|
*/
|
|
static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
|
|
static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
|
|
|
|
+ __releases(dcache_lock)
|
|
{
|
|
{
|
|
struct mutex *m1 = NULL, *m2 = NULL;
|
|
struct mutex *m1 = NULL, *m2 = NULL;
|
|
struct dentry *ret;
|
|
struct dentry *ret;
|
|
@@ -1743,11 +1745,9 @@ out_nolock:
|
|
shouldnt_be_hashed:
|
|
shouldnt_be_hashed:
|
|
spin_unlock(&dcache_lock);
|
|
spin_unlock(&dcache_lock);
|
|
BUG();
|
|
BUG();
|
|
- goto shouldnt_be_hashed;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static int prepend(char **buffer, int *buflen, const char *str,
|
|
|
|
- int namelen)
|
|
|
|
|
|
+static int prepend(char **buffer, int *buflen, const char *str, int namelen)
|
|
{
|
|
{
|
|
*buflen -= namelen;
|
|
*buflen -= namelen;
|
|
if (*buflen < 0)
|
|
if (*buflen < 0)
|
|
@@ -1757,8 +1757,13 @@ static int prepend(char **buffer, int *buflen, const char *str,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int prepend_name(char **buffer, int *buflen, struct qstr *name)
|
|
|
|
+{
|
|
|
|
+ return prepend(buffer, buflen, name->name, name->len);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
- * d_path - return the path of a dentry
|
|
|
|
|
|
+ * __d_path - return the path of a dentry
|
|
* @path: the dentry/vfsmount to report
|
|
* @path: the dentry/vfsmount to report
|
|
* @root: root vfsmnt/dentry (may be modified by this function)
|
|
* @root: root vfsmnt/dentry (may be modified by this function)
|
|
* @buffer: buffer to return value in
|
|
* @buffer: buffer to return value in
|
|
@@ -1779,9 +1784,10 @@ char *__d_path(const struct path *path, struct path *root,
|
|
{
|
|
{
|
|
struct dentry *dentry = path->dentry;
|
|
struct dentry *dentry = path->dentry;
|
|
struct vfsmount *vfsmnt = path->mnt;
|
|
struct vfsmount *vfsmnt = path->mnt;
|
|
- char * end = buffer+buflen;
|
|
|
|
- char * retval;
|
|
|
|
|
|
+ char *end = buffer + buflen;
|
|
|
|
+ char *retval;
|
|
|
|
|
|
|
|
+ spin_lock(&vfsmount_lock);
|
|
prepend(&end, &buflen, "\0", 1);
|
|
prepend(&end, &buflen, "\0", 1);
|
|
if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
|
|
if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
|
|
(prepend(&end, &buflen, " (deleted)", 10) != 0))
|
|
(prepend(&end, &buflen, " (deleted)", 10) != 0))
|
|
@@ -1800,38 +1806,37 @@ char *__d_path(const struct path *path, struct path *root,
|
|
break;
|
|
break;
|
|
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
|
|
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
|
|
/* Global root? */
|
|
/* Global root? */
|
|
- spin_lock(&vfsmount_lock);
|
|
|
|
if (vfsmnt->mnt_parent == vfsmnt) {
|
|
if (vfsmnt->mnt_parent == vfsmnt) {
|
|
- spin_unlock(&vfsmount_lock);
|
|
|
|
goto global_root;
|
|
goto global_root;
|
|
}
|
|
}
|
|
dentry = vfsmnt->mnt_mountpoint;
|
|
dentry = vfsmnt->mnt_mountpoint;
|
|
vfsmnt = vfsmnt->mnt_parent;
|
|
vfsmnt = vfsmnt->mnt_parent;
|
|
- spin_unlock(&vfsmount_lock);
|
|
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
parent = dentry->d_parent;
|
|
parent = dentry->d_parent;
|
|
prefetch(parent);
|
|
prefetch(parent);
|
|
- if ((prepend(&end, &buflen, dentry->d_name.name,
|
|
|
|
- dentry->d_name.len) != 0) ||
|
|
|
|
|
|
+ if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
|
|
(prepend(&end, &buflen, "/", 1) != 0))
|
|
(prepend(&end, &buflen, "/", 1) != 0))
|
|
goto Elong;
|
|
goto Elong;
|
|
retval = end;
|
|
retval = end;
|
|
dentry = parent;
|
|
dentry = parent;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+out:
|
|
|
|
+ spin_unlock(&vfsmount_lock);
|
|
return retval;
|
|
return retval;
|
|
|
|
|
|
global_root:
|
|
global_root:
|
|
retval += 1; /* hit the slash */
|
|
retval += 1; /* hit the slash */
|
|
- if (prepend(&retval, &buflen, dentry->d_name.name,
|
|
|
|
- dentry->d_name.len) != 0)
|
|
|
|
|
|
+ if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
|
|
goto Elong;
|
|
goto Elong;
|
|
root->mnt = vfsmnt;
|
|
root->mnt = vfsmnt;
|
|
root->dentry = dentry;
|
|
root->dentry = dentry;
|
|
- return retval;
|
|
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
Elong:
|
|
Elong:
|
|
- return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
|
|
+ retval = ERR_PTR(-ENAMETOOLONG);
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -1845,9 +1850,9 @@ Elong:
|
|
*
|
|
*
|
|
* Returns the buffer or an error code if the path was too long.
|
|
* Returns the buffer or an error code if the path was too long.
|
|
*
|
|
*
|
|
- * "buflen" should be positive. Caller holds the dcache_lock.
|
|
|
|
|
|
+ * "buflen" should be positive.
|
|
*/
|
|
*/
|
|
-char *d_path(struct path *path, char *buf, int buflen)
|
|
|
|
|
|
+char *d_path(const struct path *path, char *buf, int buflen)
|
|
{
|
|
{
|
|
char *res;
|
|
char *res;
|
|
struct path root;
|
|
struct path root;
|
|
@@ -1915,16 +1920,11 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
|
|
retval = end-1;
|
|
retval = end-1;
|
|
*retval = '/';
|
|
*retval = '/';
|
|
|
|
|
|
- for (;;) {
|
|
|
|
- struct dentry *parent;
|
|
|
|
- if (IS_ROOT(dentry))
|
|
|
|
- break;
|
|
|
|
|
|
+ while (!IS_ROOT(dentry)) {
|
|
|
|
+ struct dentry *parent = dentry->d_parent;
|
|
|
|
|
|
- parent = dentry->d_parent;
|
|
|
|
prefetch(parent);
|
|
prefetch(parent);
|
|
-
|
|
|
|
- if ((prepend(&end, &buflen, dentry->d_name.name,
|
|
|
|
- dentry->d_name.len) != 0) ||
|
|
|
|
|
|
+ if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
|
|
(prepend(&end, &buflen, "/", 1) != 0))
|
|
(prepend(&end, &buflen, "/", 1) != 0))
|
|
goto Elong;
|
|
goto Elong;
|
|
|
|
|
|
@@ -1975,7 +1975,7 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
|
|
error = -ENOENT;
|
|
error = -ENOENT;
|
|
/* Has the current directory has been unlinked? */
|
|
/* Has the current directory has been unlinked? */
|
|
spin_lock(&dcache_lock);
|
|
spin_lock(&dcache_lock);
|
|
- if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
|
|
|
|
|
|
+ if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) {
|
|
unsigned long len;
|
|
unsigned long len;
|
|
struct path tmp = root;
|
|
struct path tmp = root;
|
|
char * cwd;
|
|
char * cwd;
|