mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-17 23:25:46 +08:00
Merge branch 'vfs-scale-working' of git://git.kernel.org/pub/scm/linux/kernel/git/npiggin/linux-npiggin
* 'vfs-scale-working' of git://git.kernel.org/pub/scm/linux/kernel/git/npiggin/linux-npiggin: (57 commits) fs: scale mntget/mntput fs: rename vfsmount counter helpers fs: implement faster dentry memcmp fs: prefetch inode data in dcache lookup fs: improve scalability of pseudo filesystems fs: dcache per-inode inode alias locking fs: dcache per-bucket dcache hash locking bit_spinlock: add required includes kernel: add bl_list xfs: provide simple rcu-walk ACL implementation btrfs: provide simple rcu-walk ACL implementation ext2,3,4: provide simple rcu-walk ACL implementation fs: provide simple rcu-walk generic_check_acl implementation fs: provide rcu-walk aware permission i_ops fs: rcu-walk aware d_revalidate method fs: cache optimise dentry and inode for rcu-walk fs: dcache reduce branches in lookup path fs: dcache remove d_mounted fs: fs_struct use seqlock fs: rcu-walk for path lookup ...
This commit is contained in:
commit
b4a45f5fe8
@ -9,22 +9,25 @@ be able to use diff(1).
|
||||
|
||||
--------------------------- dentry_operations --------------------------
|
||||
prototypes:
|
||||
int (*d_revalidate)(struct dentry *, int);
|
||||
int (*d_hash) (struct dentry *, struct qstr *);
|
||||
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
|
||||
int (*d_revalidate)(struct dentry *, struct nameidata *);
|
||||
int (*d_hash)(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
int (*d_delete)(struct dentry *);
|
||||
void (*d_release)(struct dentry *);
|
||||
void (*d_iput)(struct dentry *, struct inode *);
|
||||
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
|
||||
|
||||
locking rules:
|
||||
dcache_lock rename_lock ->d_lock may block
|
||||
d_revalidate: no no no yes
|
||||
d_hash no no no yes
|
||||
d_compare: no yes no no
|
||||
d_delete: yes no yes no
|
||||
d_release: no no no yes
|
||||
d_iput: no no no yes
|
||||
rename_lock ->d_lock may block rcu-walk
|
||||
d_revalidate: no no yes (ref-walk) maybe
|
||||
d_hash no no no maybe
|
||||
d_compare: yes no no maybe
|
||||
d_delete: no yes no no
|
||||
d_release: no no yes no
|
||||
d_iput: no no yes no
|
||||
d_dname: no no no no
|
||||
|
||||
--------------------------- inode_operations ---------------------------
|
||||
@ -44,8 +47,8 @@ ata *);
|
||||
void * (*follow_link) (struct dentry *, struct nameidata *);
|
||||
void (*put_link) (struct dentry *, struct nameidata *, void *);
|
||||
void (*truncate) (struct inode *);
|
||||
int (*permission) (struct inode *, int, struct nameidata *);
|
||||
int (*check_acl)(struct inode *, int);
|
||||
int (*permission) (struct inode *, int, unsigned int);
|
||||
int (*check_acl)(struct inode *, int, unsigned int);
|
||||
int (*setattr) (struct dentry *, struct iattr *);
|
||||
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
|
||||
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
||||
@ -73,7 +76,7 @@ follow_link: no
|
||||
put_link: no
|
||||
truncate: yes (see below)
|
||||
setattr: yes
|
||||
permission: no
|
||||
permission: no (may not block if called in rcu-walk mode)
|
||||
check_acl: no
|
||||
getattr: no
|
||||
setxattr: yes
|
||||
|
@ -1,174 +0,0 @@
|
||||
RCU-based dcache locking model
|
||||
==============================
|
||||
|
||||
On many workloads, the most common operation on dcache is to look up a
|
||||
dentry, given a parent dentry and the name of the child. Typically,
|
||||
for every open(), stat() etc., the dentry corresponding to the
|
||||
pathname will be looked up by walking the tree starting with the first
|
||||
component of the pathname and using that dentry along with the next
|
||||
component to look up the next level and so on. Since it is a frequent
|
||||
operation for workloads like multiuser environments and web servers,
|
||||
it is important to optimize this path.
|
||||
|
||||
Prior to 2.5.10, dcache_lock was acquired in d_lookup and thus in
|
||||
every component during path look-up. Since 2.5.10 onwards, fast-walk
|
||||
algorithm changed this by holding the dcache_lock at the beginning and
|
||||
walking as many cached path component dentries as possible. This
|
||||
significantly decreases the number of acquisition of
|
||||
dcache_lock. However it also increases the lock hold time
|
||||
significantly and affects performance in large SMP machines. Since
|
||||
2.5.62 kernel, dcache has been using a new locking model that uses RCU
|
||||
to make dcache look-up lock-free.
|
||||
|
||||
The current dcache locking model is not very different from the
|
||||
existing dcache locking model. Prior to 2.5.62 kernel, dcache_lock
|
||||
protected the hash chain, d_child, d_alias, d_lru lists as well as
|
||||
d_inode and several other things like mount look-up. RCU-based changes
|
||||
affect only the way the hash chain is protected. For everything else
|
||||
the dcache_lock must be taken for both traversing as well as
|
||||
updating. The hash chain updates too take the dcache_lock. The
|
||||
significant change is the way d_lookup traverses the hash chain, it
|
||||
doesn't acquire the dcache_lock for this and rely on RCU to ensure
|
||||
that the dentry has not been *freed*.
|
||||
|
||||
|
||||
Dcache locking details
|
||||
======================
|
||||
|
||||
For many multi-user workloads, open() and stat() on files are very
|
||||
frequently occurring operations. Both involve walking of path names to
|
||||
find the dentry corresponding to the concerned file. In 2.4 kernel,
|
||||
dcache_lock was held during look-up of each path component. Contention
|
||||
and cache-line bouncing of this global lock caused significant
|
||||
scalability problems. With the introduction of RCU in Linux kernel,
|
||||
this was worked around by making the look-up of path components during
|
||||
path walking lock-free.
|
||||
|
||||
|
||||
Safe lock-free look-up of dcache hash table
|
||||
===========================================
|
||||
|
||||
Dcache is a complex data structure with the hash table entries also
|
||||
linked together in other lists. In 2.4 kernel, dcache_lock protected
|
||||
all the lists. We applied RCU only on hash chain walking. The rest of
|
||||
the lists are still protected by dcache_lock. Some of the important
|
||||
changes are :
|
||||
|
||||
1. The deletion from hash chain is done using hlist_del_rcu() macro
|
||||
which doesn't initialize next pointer of the deleted dentry and
|
||||
this allows us to walk safely lock-free while a deletion is
|
||||
happening.
|
||||
|
||||
2. Insertion of a dentry into the hash table is done using
|
||||
hlist_add_head_rcu() which take care of ordering the writes - the
|
||||
writes to the dentry must be visible before the dentry is
|
||||
inserted. This works in conjunction with hlist_for_each_rcu(),
|
||||
which has since been replaced by hlist_for_each_entry_rcu(), while
|
||||
walking the hash chain. The only requirement is that all
|
||||
initialization to the dentry must be done before
|
||||
hlist_add_head_rcu() since we don't have dcache_lock protection
|
||||
while traversing the hash chain. This isn't different from the
|
||||
existing code.
|
||||
|
||||
3. The dentry looked up without holding dcache_lock by cannot be
|
||||
returned for walking if it is unhashed. It then may have a NULL
|
||||
d_inode or other bogosity since RCU doesn't protect the other
|
||||
fields in the dentry. We therefore use a flag DCACHE_UNHASHED to
|
||||
indicate unhashed dentries and use this in conjunction with a
|
||||
per-dentry lock (d_lock). Once looked up without the dcache_lock,
|
||||
we acquire the per-dentry lock (d_lock) and check if the dentry is
|
||||
unhashed. If so, the look-up is failed. If not, the reference count
|
||||
of the dentry is increased and the dentry is returned.
|
||||
|
||||
4. Once a dentry is looked up, it must be ensured during the path walk
|
||||
for that component it doesn't go away. In pre-2.5.10 code, this was
|
||||
done holding a reference to the dentry. dcache_rcu does the same.
|
||||
In some sense, dcache_rcu path walking looks like the pre-2.5.10
|
||||
version.
|
||||
|
||||
5. All dentry hash chain updates must take the dcache_lock as well as
|
||||
the per-dentry lock in that order. dput() does this to ensure that
|
||||
a dentry that has just been looked up in another CPU doesn't get
|
||||
deleted before dget() can be done on it.
|
||||
|
||||
6. There are several ways to do reference counting of RCU protected
|
||||
objects. One such example is in ipv4 route cache where deferred
|
||||
freeing (using call_rcu()) is done as soon as the reference count
|
||||
goes to zero. This cannot be done in the case of dentries because
|
||||
tearing down of dentries require blocking (dentry_iput()) which
|
||||
isn't supported from RCU callbacks. Instead, tearing down of
|
||||
dentries happen synchronously in dput(), but actual freeing happens
|
||||
later when RCU grace period is over. This allows safe lock-free
|
||||
walking of the hash chains, but a matched dentry may have been
|
||||
partially torn down. The checking of DCACHE_UNHASHED flag with
|
||||
d_lock held detects such dentries and prevents them from being
|
||||
returned from look-up.
|
||||
|
||||
|
||||
Maintaining POSIX rename semantics
|
||||
==================================
|
||||
|
||||
Since look-up of dentries is lock-free, it can race against a
|
||||
concurrent rename operation. For example, during rename of file A to
|
||||
B, look-up of either A or B must succeed. So, if look-up of B happens
|
||||
after A has been removed from the hash chain but not added to the new
|
||||
hash chain, it may fail. Also, a comparison while the name is being
|
||||
written concurrently by a rename may result in false positive matches
|
||||
violating rename semantics. Issues related to race with rename are
|
||||
handled as described below :
|
||||
|
||||
1. Look-up can be done in two ways - d_lookup() which is safe from
|
||||
simultaneous renames and __d_lookup() which is not. If
|
||||
__d_lookup() fails, it must be followed up by a d_lookup() to
|
||||
correctly determine whether a dentry is in the hash table or
|
||||
not. d_lookup() protects look-ups using a sequence lock
|
||||
(rename_lock).
|
||||
|
||||
2. The name associated with a dentry (d_name) may be changed if a
|
||||
rename is allowed to happen simultaneously. To avoid memcmp() in
|
||||
__d_lookup() go out of bounds due to a rename and false positive
|
||||
comparison, the name comparison is done while holding the
|
||||
per-dentry lock. This prevents concurrent renames during this
|
||||
operation.
|
||||
|
||||
3. Hash table walking during look-up may move to a different bucket as
|
||||
the current dentry is moved to a different bucket due to rename.
|
||||
But we use hlists in dcache hash table and they are
|
||||
null-terminated. So, even if a dentry moves to a different bucket,
|
||||
hash chain walk will terminate. [with a list_head list, it may not
|
||||
since termination is when the list_head in the original bucket is
|
||||
reached]. Since we redo the d_parent check and compare name while
|
||||
holding d_lock, lock-free look-up will not race against d_move().
|
||||
|
||||
4. There can be a theoretical race when a dentry keeps coming back to
|
||||
original bucket due to double moves. Due to this look-up may
|
||||
consider that it has never moved and can end up in a infinite loop.
|
||||
But this is not any worse that theoretical livelocks we already
|
||||
have in the kernel.
|
||||
|
||||
|
||||
Important guidelines for filesystem developers related to dcache_rcu
|
||||
====================================================================
|
||||
|
||||
1. Existing dcache interfaces (pre-2.5.62) exported to filesystem
|
||||
don't change. Only dcache internal implementation changes. However
|
||||
filesystems *must not* delete from the dentry hash chains directly
|
||||
using the list macros like allowed earlier. They must use dcache
|
||||
APIs like d_drop() or __d_drop() depending on the situation.
|
||||
|
||||
2. d_flags is now protected by a per-dentry lock (d_lock). All access
|
||||
to d_flags must be protected by it.
|
||||
|
||||
3. For a hashed dentry, checking of d_count needs to be protected by
|
||||
d_lock.
|
||||
|
||||
|
||||
Papers and other documentation on dcache locking
|
||||
================================================
|
||||
|
||||
1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
|
||||
|
||||
2. http://lse.sourceforge.net/locking/dcache/dcache.html
|
||||
|
||||
|
||||
|
382
Documentation/filesystems/path-lookup.txt
Normal file
382
Documentation/filesystems/path-lookup.txt
Normal file
@ -0,0 +1,382 @@
|
||||
Path walking and name lookup locking
|
||||
====================================
|
||||
|
||||
Path resolution is the finding a dentry corresponding to a path name string, by
|
||||
performing a path walk. Typically, for every open(), stat() etc., the path name
|
||||
will be resolved. Paths are resolved by walking the namespace tree, starting
|
||||
with the first component of the pathname (eg. root or cwd) with a known dentry,
|
||||
then finding the child of that dentry, which is named the next component in the
|
||||
path string. Then repeating the lookup from the child dentry and finding its
|
||||
child with the next element, and so on.
|
||||
|
||||
Since it is a frequent operation for workloads like multiuser environments and
|
||||
web servers, it is important to optimize this code.
|
||||
|
||||
Path walking synchronisation history:
|
||||
Prior to 2.5.10, dcache_lock was acquired in d_lookup (dcache hash lookup) and
|
||||
thus in every component during path look-up. Since 2.5.10 onwards, fast-walk
|
||||
algorithm changed this by holding the dcache_lock at the beginning and walking
|
||||
as many cached path component dentries as possible. This significantly
|
||||
decreases the number of acquisition of dcache_lock. However it also increases
|
||||
the lock hold time significantly and affects performance in large SMP machines.
|
||||
Since 2.5.62 kernel, dcache has been using a new locking model that uses RCU to
|
||||
make dcache look-up lock-free.
|
||||
|
||||
All the above algorithms required taking a lock and reference count on the
|
||||
dentry that was looked up, so that may be used as the basis for walking the
|
||||
next path element. This is inefficient and unscalable. It is inefficient
|
||||
because of the locks and atomic operations required for every dentry element
|
||||
slows things down. It is not scalable because many parallel applications that
|
||||
are path-walk intensive tend to do path lookups starting from a common dentry
|
||||
(usually, the root "/" or current working directory). So contention on these
|
||||
common path elements causes lock and cacheline queueing.
|
||||
|
||||
Since 2.6.38, RCU is used to make a significant part of the entire path walk
|
||||
(including dcache look-up) completely "store-free" (so, no locks, atomics, or
|
||||
even stores into cachelines of common dentries). This is known as "rcu-walk"
|
||||
path walking.
|
||||
|
||||
Path walking overview
|
||||
=====================
|
||||
|
||||
A name string specifies a start (root directory, cwd, fd-relative) and a
|
||||
sequence of elements (directory entry names), which together refer to a path in
|
||||
the namespace. A path is represented as a (dentry, vfsmount) tuple. The name
|
||||
elements are sub-strings, seperated by '/'.
|
||||
|
||||
Name lookups will want to find a particular path that a name string refers to
|
||||
(usually the final element, or parent of final element). This is done by taking
|
||||
the path given by the name's starting point (which we know in advance -- eg.
|
||||
current->fs->cwd or current->fs->root) as the first parent of the lookup. Then
|
||||
iteratively for each subsequent name element, look up the child of the current
|
||||
parent with the given name and if it is not the desired entry, make it the
|
||||
parent for the next lookup.
|
||||
|
||||
A parent, of course, must be a directory, and we must have appropriate
|
||||
permissions on the parent inode to be able to walk into it.
|
||||
|
||||
Turning the child into a parent for the next lookup requires more checks and
|
||||
procedures. Symlinks essentially substitute the symlink name for the target
|
||||
name in the name string, and require some recursive path walking. Mount points
|
||||
must be followed into (thus changing the vfsmount that subsequent path elements
|
||||
refer to), switching from the mount point path to the root of the particular
|
||||
mounted vfsmount. These behaviours are variously modified depending on the
|
||||
exact path walking flags.
|
||||
|
||||
Path walking then must, broadly, do several particular things:
|
||||
- find the start point of the walk;
|
||||
- perform permissions and validity checks on inodes;
|
||||
- perform dcache hash name lookups on (parent, name element) tuples;
|
||||
- traverse mount points;
|
||||
- traverse symlinks;
|
||||
- lookup and create missing parts of the path on demand.
|
||||
|
||||
Safe store-free look-up of dcache hash table
|
||||
============================================
|
||||
|
||||
Dcache name lookup
|
||||
------------------
|
||||
In order to lookup a dcache (parent, name) tuple, we take a hash on the tuple
|
||||
and use that to select a bucket in the dcache-hash table. The list of entries
|
||||
in that bucket is then walked, and we do a full comparison of each entry
|
||||
against our (parent, name) tuple.
|
||||
|
||||
The hash lists are RCU protected, so list walking is not serialised with
|
||||
concurrent updates (insertion, deletion from the hash). This is a standard RCU
|
||||
list application with the exception of renames, which will be covered below.
|
||||
|
||||
Parent and name members of a dentry, as well as its membership in the dcache
|
||||
hash, and its inode are protected by the per-dentry d_lock spinlock. A
|
||||
reference is taken on the dentry (while the fields are verified under d_lock),
|
||||
and this stabilises its d_inode pointer and actual inode. This gives a stable
|
||||
point to perform the next step of our path walk against.
|
||||
|
||||
These members are also protected by d_seq seqlock, although this offers
|
||||
read-only protection and no durability of results, so care must be taken when
|
||||
using d_seq for synchronisation (see seqcount based lookups, below).
|
||||
|
||||
Renames
|
||||
-------
|
||||
Back to the rename case. In usual RCU protected lists, the only operations that
|
||||
will happen to an object is insertion, and then eventually removal from the
|
||||
list. The object will not be reused until an RCU grace period is complete.
|
||||
This ensures the RCU list traversal primitives can run over the object without
|
||||
problems (see RCU documentation for how this works).
|
||||
|
||||
However when a dentry is renamed, its hash value can change, requiring it to be
|
||||
moved to a new hash list. Allocating and inserting a new alias would be
|
||||
expensive and also problematic for directory dentries. Latency would be far to
|
||||
high to wait for a grace period after removing the dentry and before inserting
|
||||
it in the new hash bucket. So what is done is to insert the dentry into the
|
||||
new list immediately.
|
||||
|
||||
However, when the dentry's list pointers are updated to point to objects in the
|
||||
new list before waiting for a grace period, this can result in a concurrent RCU
|
||||
lookup of the old list veering off into the new (incorrect) list and missing
|
||||
the remaining dentries on the list.
|
||||
|
||||
There is no fundamental problem with walking down the wrong list, because the
|
||||
dentry comparisons will never match. However it is fatal to miss a matching
|
||||
dentry. So a seqlock is used to detect when a rename has occurred, and so the
|
||||
lookup can be retried.
|
||||
|
||||
1 2 3
|
||||
+---+ +---+ +---+
|
||||
hlist-->| N-+->| N-+->| N-+->
|
||||
head <--+-P |<-+-P |<-+-P |
|
||||
+---+ +---+ +---+
|
||||
|
||||
Rename of dentry 2 may require it deleted from the above list, and inserted
|
||||
into a new list. Deleting 2 gives the following list.
|
||||
|
||||
1 3
|
||||
+---+ +---+ (don't worry, the longer pointers do not
|
||||
hlist-->| N-+-------->| N-+-> impose a measurable performance overhead
|
||||
head <--+-P |<--------+-P | on modern CPUs)
|
||||
+---+ +---+
|
||||
^ 2 ^
|
||||
| +---+ |
|
||||
| | N-+----+
|
||||
+----+-P |
|
||||
+---+
|
||||
|
||||
This is a standard RCU-list deletion, which leaves the deleted object's
|
||||
pointers intact, so a concurrent list walker that is currently looking at
|
||||
object 2 will correctly continue to object 3 when it is time to traverse the
|
||||
next object.
|
||||
|
||||
However, when inserting object 2 onto a new list, we end up with this:
|
||||
|
||||
1 3
|
||||
+---+ +---+
|
||||
hlist-->| N-+-------->| N-+->
|
||||
head <--+-P |<--------+-P |
|
||||
+---+ +---+
|
||||
2
|
||||
+---+
|
||||
| N-+---->
|
||||
<----+-P |
|
||||
+---+
|
||||
|
||||
Because we didn't wait for a grace period, there may be a concurrent lookup
|
||||
still at 2. Now when it follows 2's 'next' pointer, it will walk off into
|
||||
another list without ever having checked object 3.
|
||||
|
||||
A related, but distinctly different, issue is that of rename atomicity versus
|
||||
lookup operations. If a file is renamed from 'A' to 'B', a lookup must only
|
||||
find either 'A' or 'B'. So if a lookup of 'A' returns NULL, a subsequent lookup
|
||||
of 'B' must succeed (note the reverse is not true).
|
||||
|
||||
Between deleting the dentry from the old hash list, and inserting it on the new
|
||||
hash list, a lookup may find neither 'A' nor 'B' matching the dentry. The same
|
||||
rename seqlock is also used to cover this race in much the same way, by
|
||||
retrying a negative lookup result if a rename was in progress.
|
||||
|
||||
Seqcount based lookups
|
||||
----------------------
|
||||
In refcount based dcache lookups, d_lock is used to serialise access to
|
||||
the dentry, stabilising it while comparing its name and parent and then
|
||||
taking a reference count (the reference count then gives a stable place to
|
||||
start the next part of the path walk from).
|
||||
|
||||
As explained above, we would like to do path walking without taking locks or
|
||||
reference counts on intermediate dentries along the path. To do this, a per
|
||||
dentry seqlock (d_seq) is used to take a "coherent snapshot" of what the dentry
|
||||
looks like (its name, parent, and inode). That snapshot is then used to start
|
||||
the next part of the path walk. When loading the coherent snapshot under d_seq,
|
||||
care must be taken to load the members up-front, and use those pointers rather
|
||||
than reloading from the dentry later on (otherwise we'd have interesting things
|
||||
like d_inode going NULL underneath us, if the name was unlinked).
|
||||
|
||||
Also important is to avoid performing any destructive operations (pretty much:
|
||||
no non-atomic stores to shared data), and to recheck the seqcount when we are
|
||||
"done" with the operation. Retry or abort if the seqcount does not match.
|
||||
Avoiding destructive or changing operations means we can easily unwind from
|
||||
failure.
|
||||
|
||||
What this means is that a caller, provided they are holding RCU lock to
|
||||
protect the dentry object from disappearing, can perform a seqcount based
|
||||
lookup which does not increment the refcount on the dentry or write to
|
||||
it in any way. This returned dentry can be used for subsequent operations,
|
||||
provided that d_seq is rechecked after that operation is complete.
|
||||
|
||||
Inodes are also rcu freed, so the seqcount lookup dentry's inode may also be
|
||||
queried for permissions.
|
||||
|
||||
With this two parts of the puzzle, we can do path lookups without taking
|
||||
locks or refcounts on dentry elements.
|
||||
|
||||
RCU-walk path walking design
|
||||
============================
|
||||
|
||||
Path walking code now has two distinct modes, ref-walk and rcu-walk. ref-walk
|
||||
is the traditional[*] way of performing dcache lookups using d_lock to
|
||||
serialise concurrent modifications to the dentry and take a reference count on
|
||||
it. ref-walk is simple and obvious, and may sleep, take locks, etc while path
|
||||
walking is operating on each dentry. rcu-walk uses seqcount based dentry
|
||||
lookups, and can perform lookup of intermediate elements without any stores to
|
||||
shared data in the dentry or inode. rcu-walk can not be applied to all cases,
|
||||
eg. if the filesystem must sleep or perform non trivial operations, rcu-walk
|
||||
must be switched to ref-walk mode.
|
||||
|
||||
[*] RCU is still used for the dentry hash lookup in ref-walk, but not the full
|
||||
path walk.
|
||||
|
||||
Where ref-walk uses a stable, refcounted ``parent'' to walk the remaining
|
||||
path string, rcu-walk uses a d_seq protected snapshot. When looking up a
|
||||
child of this parent snapshot, we open d_seq critical section on the child
|
||||
before closing d_seq critical section on the parent. This gives an interlocking
|
||||
ladder of snapshots to walk down.
|
||||
|
||||
|
||||
proc 101
|
||||
/----------------\
|
||||
/ comm: "vi" \
|
||||
/ fs.root: dentry0 \
|
||||
\ fs.cwd: dentry2 /
|
||||
\ /
|
||||
\----------------/
|
||||
|
||||
So when vi wants to open("/home/npiggin/test.c", O_RDWR), then it will
|
||||
start from current->fs->root, which is a pinned dentry. Alternatively,
|
||||
"./test.c" would start from cwd; both names refer to the same path in
|
||||
the context of proc101.
|
||||
|
||||
dentry 0
|
||||
+---------------------+ rcu-walk begins here, we note d_seq, check the
|
||||
| name: "/" | inode's permission, and then look up the next
|
||||
| inode: 10 | path element which is "home"...
|
||||
| children:"home", ...|
|
||||
+---------------------+
|
||||
|
|
||||
dentry 1 V
|
||||
+---------------------+ ... which brings us here. We find dentry1 via
|
||||
| name: "home" | hash lookup, then note d_seq and compare name
|
||||
| inode: 678 | string and parent pointer. When we have a match,
|
||||
| children:"npiggin" | we now recheck the d_seq of dentry0. Then we
|
||||
+---------------------+ check inode and look up the next element.
|
||||
|
|
||||
dentry2 V
|
||||
+---------------------+ Note: if dentry0 is now modified, lookup is
|
||||
| name: "npiggin" | not necessarily invalid, so we need only keep a
|
||||
| inode: 543 | parent for d_seq verification, and grandparents
|
||||
| children:"a.c", ... | can be forgotten.
|
||||
+---------------------+
|
||||
|
|
||||
dentry3 V
|
||||
+---------------------+ At this point we have our destination dentry.
|
||||
| name: "a.c" | We now take its d_lock, verify d_seq of this
|
||||
| inode: 14221 | dentry. If that checks out, we can increment
|
||||
| children:NULL | its refcount because we're holding d_lock.
|
||||
+---------------------+
|
||||
|
||||
Taking a refcount on a dentry from rcu-walk mode, by taking its d_lock,
|
||||
re-checking its d_seq, and then incrementing its refcount is called
|
||||
"dropping rcu" or dropping from rcu-walk into ref-walk mode.
|
||||
|
||||
It is, in some sense, a bit of a house of cards. If the seqcount check of the
|
||||
parent snapshot fails, the house comes down, because we had closed the d_seq
|
||||
section on the grandparent, so we have nothing left to stand on. In that case,
|
||||
the path walk must be fully restarted (which we do in ref-walk mode, to avoid
|
||||
live locks). It is costly to have a full restart, but fortunately they are
|
||||
quite rare.
|
||||
|
||||
When we reach a point where sleeping is required, or a filesystem callout
|
||||
requires ref-walk, then instead of restarting the walk, we attempt to drop rcu
|
||||
at the last known good dentry we have. Avoiding a full restart in ref-walk in
|
||||
these cases is fundamental for performance and scalability because blocking
|
||||
operations such as creates and unlinks are not uncommon.
|
||||
|
||||
The detailed design for rcu-walk is like this:
|
||||
* LOOKUP_RCU is set in nd->flags, which distinguishes rcu-walk from ref-walk.
|
||||
* Take the RCU lock for the entire path walk, starting with the acquiring
|
||||
of the starting path (eg. root/cwd/fd-path). So now dentry refcounts are
|
||||
not required for dentry persistence.
|
||||
* synchronize_rcu is called when unregistering a filesystem, so we can
|
||||
access d_ops and i_ops during rcu-walk.
|
||||
* Similarly take the vfsmount lock for the entire path walk. So now mnt
|
||||
refcounts are not required for persistence. Also we are free to perform mount
|
||||
lookups, and to assume dentry mount points and mount roots are stable up and
|
||||
down the path.
|
||||
* Have a per-dentry seqlock to protect the dentry name, parent, and inode,
|
||||
so we can load this tuple atomically, and also check whether any of its
|
||||
members have changed.
|
||||
* Dentry lookups (based on parent, candidate string tuple) recheck the parent
|
||||
sequence after the child is found in case anything changed in the parent
|
||||
during the path walk.
|
||||
* inode is also RCU protected so we can load d_inode and use the inode for
|
||||
limited things.
|
||||
* i_mode, i_uid, i_gid can be tested for exec permissions during path walk.
|
||||
* i_op can be loaded.
|
||||
* When the destination dentry is reached, drop rcu there (ie. take d_lock,
|
||||
verify d_seq, increment refcount).
|
||||
* If seqlock verification fails anywhere along the path, do a full restart
|
||||
of the path lookup in ref-walk mode. -ECHILD tends to be used (for want of
|
||||
a better errno) to signal an rcu-walk failure.
|
||||
|
||||
The cases where rcu-walk cannot continue are:
|
||||
* NULL dentry (ie. any uncached path element)
|
||||
* Following links
|
||||
|
||||
It may be possible eventually to make following links rcu-walk aware.
|
||||
|
||||
Uncached path elements will always require dropping to ref-walk mode, at the
|
||||
very least because i_mutex needs to be grabbed, and objects allocated.
|
||||
|
||||
Final note:
|
||||
"store-free" path walking is not strictly store free. We take vfsmount lock
|
||||
and refcounts (both of which can be made per-cpu), and we also store to the
|
||||
stack (which is essentially CPU-local), and we also have to take locks and
|
||||
refcount on final dentry.
|
||||
|
||||
The point is that shared data, where practically possible, is not locked
|
||||
or stored into. The result is massive improvements in performance and
|
||||
scalability of path resolution.
|
||||
|
||||
|
||||
Interesting statistics
|
||||
======================
|
||||
|
||||
The following table gives rcu lookup statistics for a few simple workloads
|
||||
(2s12c24t Westmere, debian non-graphical system). Ungraceful are attempts to
|
||||
drop rcu that fail due to d_seq failure and requiring the entire path lookup
|
||||
again. Other cases are successful rcu-drops that are required before the final
|
||||
element, nodentry for missing dentry, revalidate for filesystem revalidate
|
||||
routine requiring rcu drop, permission for permission check requiring drop,
|
||||
and link for symlink traversal requiring drop.
|
||||
|
||||
rcu-lookups restart nodentry link revalidate permission
|
||||
bootup 47121 0 4624 1010 10283 7852
|
||||
dbench 25386793 0 6778659(26.7%) 55 549 1156
|
||||
kbuild 2696672 10 64442(2.3%) 108764(4.0%) 1 1590
|
||||
git diff 39605 0 28 2 0 106
|
||||
vfstest 24185492 4945 708725(2.9%) 1076136(4.4%) 0 2651
|
||||
|
||||
What this shows is that failed rcu-walk lookups, ie. ones that are restarted
|
||||
entirely with ref-walk, are quite rare. Even the "vfstest" case which
|
||||
specifically has concurrent renames/mkdir/rmdir/ creat/unlink/etc to excercise
|
||||
such races is not showing a huge amount of restarts.
|
||||
|
||||
Dropping from rcu-walk to ref-walk mean that we have encountered a dentry where
|
||||
the reference count needs to be taken for some reason. This is either because
|
||||
we have reached the target of the path walk, or because we have encountered a
|
||||
condition that can't be resolved in rcu-walk mode. Ideally, we drop rcu-walk
|
||||
only when we have reached the target dentry, so the other statistics show where
|
||||
this does not happen.
|
||||
|
||||
Note that a graceful drop from rcu-walk mode due to something such as the
|
||||
dentry not existing (which can be common) is not necessarily a failure of
|
||||
rcu-walk scheme, because some elements of the path may have been walked in
|
||||
rcu-walk mode. The further we get from common path elements (such as cwd or
|
||||
root), the less contended the dentry is likely to be. The closer we are to
|
||||
common path elements, the more likely they will exist in dentry cache.
|
||||
|
||||
|
||||
Papers and other documentation on dcache locking
|
||||
================================================
|
||||
|
||||
1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
|
||||
|
||||
2. http://lse.sourceforge.net/locking/dcache/dcache.html
|
||||
|
||||
|
@ -216,7 +216,6 @@ had ->revalidate()) add calls in ->follow_link()/->readlink().
|
||||
->d_parent changes are not protected by BKL anymore. Read access is safe
|
||||
if at least one of the following is true:
|
||||
* filesystem has no cross-directory rename()
|
||||
* dcache_lock is held
|
||||
* we know that parent had been locked (e.g. we are looking at
|
||||
->d_parent of ->lookup() argument).
|
||||
* we are called from ->rename().
|
||||
@ -318,3 +317,71 @@ if it's zero is not *and* *never* *had* *been* enough. Final unlink() and iput(
|
||||
may happen while the inode is in the middle of ->write_inode(); e.g. if you blindly
|
||||
free the on-disk inode, you may end up doing that while ->write_inode() is writing
|
||||
to it.
|
||||
|
||||
---
|
||||
[mandatory]
|
||||
|
||||
.d_delete() now only advises the dcache as to whether or not to cache
|
||||
unreferenced dentries, and is now only called when the dentry refcount goes to
|
||||
0. Even on 0 refcount transition, it must be able to tolerate being called 0,
|
||||
1, or more times (eg. constant, idempotent).
|
||||
|
||||
---
|
||||
[mandatory]
|
||||
|
||||
.d_compare() calling convention and locking rules are significantly
|
||||
changed. Read updated documentation in Documentation/filesystems/vfs.txt (and
|
||||
look at examples of other filesystems) for guidance.
|
||||
|
||||
---
|
||||
[mandatory]
|
||||
|
||||
.d_hash() calling convention and locking rules are significantly
|
||||
changed. Read updated documentation in Documentation/filesystems/vfs.txt (and
|
||||
look at examples of other filesystems) for guidance.
|
||||
|
||||
---
|
||||
[mandatory]
|
||||
dcache_lock is gone, replaced by fine grained locks. See fs/dcache.c
|
||||
for details of what locks to replace dcache_lock with in order to protect
|
||||
particular things. Most of the time, a filesystem only needs ->d_lock, which
|
||||
protects *all* the dcache state of a given dentry.
|
||||
|
||||
--
|
||||
[mandatory]
|
||||
|
||||
Filesystems must RCU-free their inodes, if they can have been accessed
|
||||
via rcu-walk path walk (basically, if the file can have had a path name in the
|
||||
vfs namespace).
|
||||
|
||||
i_dentry and i_rcu share storage in a union, and the vfs expects
|
||||
i_dentry to be reinitialized before it is freed, so an:
|
||||
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
|
||||
must be done in the RCU callback.
|
||||
|
||||
--
|
||||
[recommended]
|
||||
vfs now tries to do path walking in "rcu-walk mode", which avoids
|
||||
atomic operations and scalability hazards on dentries and inodes (see
|
||||
Documentation/filesystems/path-walk.txt). d_hash and d_compare changes (above)
|
||||
are examples of the changes required to support this. For more complex
|
||||
filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so
|
||||
no changes are required to the filesystem. However, this is costly and loses
|
||||
the benefits of rcu-walk mode. We will begin to add filesystem callbacks that
|
||||
are rcu-walk aware, shown below. Filesystems should take advantage of this
|
||||
where possible.
|
||||
|
||||
--
|
||||
[mandatory]
|
||||
d_revalidate is a callback that is made on every path element (if
|
||||
the filesystem provides it), which requires dropping out of rcu-walk mode. This
|
||||
may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be
|
||||
returned if the filesystem cannot handle rcu-walk. See
|
||||
Documentation/filesystems/vfs.txt for more details.
|
||||
|
||||
permission and check_acl are inode permission checks that are called
|
||||
on many or all directory inodes on the way down a path walk (to check for
|
||||
exec permission). These must now be rcu-walk aware (flags & IPERM_RCU). See
|
||||
Documentation/filesystems/vfs.txt for more details.
|
||||
|
@ -325,7 +325,8 @@ struct inode_operations {
|
||||
void * (*follow_link) (struct dentry *, struct nameidata *);
|
||||
void (*put_link) (struct dentry *, struct nameidata *, void *);
|
||||
void (*truncate) (struct inode *);
|
||||
int (*permission) (struct inode *, int, struct nameidata *);
|
||||
int (*permission) (struct inode *, int, unsigned int);
|
||||
int (*check_acl)(struct inode *, int, unsigned int);
|
||||
int (*setattr) (struct dentry *, struct iattr *);
|
||||
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
||||
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
||||
@ -414,6 +415,13 @@ otherwise noted.
|
||||
permission: called by the VFS to check for access rights on a POSIX-like
|
||||
filesystem.
|
||||
|
||||
May be called in rcu-walk mode (flags & IPERM_RCU). If in rcu-walk
|
||||
mode, the filesystem must check the permission without blocking or
|
||||
storing to the inode.
|
||||
|
||||
If a situation is encountered that rcu-walk cannot handle, return
|
||||
-ECHILD and it will be called again in ref-walk mode.
|
||||
|
||||
setattr: called by the VFS to set attributes for a file. This method
|
||||
is called by chmod(2) and related system calls.
|
||||
|
||||
@ -847,9 +855,12 @@ defined:
|
||||
|
||||
struct dentry_operations {
|
||||
int (*d_revalidate)(struct dentry *, struct nameidata *);
|
||||
int (*d_hash) (struct dentry *, struct qstr *);
|
||||
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
|
||||
int (*d_delete)(struct dentry *);
|
||||
int (*d_hash)(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
int (*d_compare)(const struct dentry *, const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
int (*d_delete)(const struct dentry *);
|
||||
void (*d_release)(struct dentry *);
|
||||
void (*d_iput)(struct dentry *, struct inode *);
|
||||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
@ -860,13 +871,45 @@ struct dentry_operations {
|
||||
dcache. Most filesystems leave this as NULL, because all their
|
||||
dentries in the dcache are valid
|
||||
|
||||
d_hash: called when the VFS adds a dentry to the hash table
|
||||
d_revalidate may be called in rcu-walk mode (nd->flags & LOOKUP_RCU).
|
||||
If in rcu-walk mode, the filesystem must revalidate the dentry without
|
||||
blocking or storing to the dentry, d_parent and d_inode should not be
|
||||
used without care (because they can go NULL), instead nd->inode should
|
||||
be used.
|
||||
|
||||
d_compare: called when a dentry should be compared with another
|
||||
If a situation is encountered that rcu-walk cannot handle, return
|
||||
-ECHILD and it will be called again in ref-walk mode.
|
||||
|
||||
d_delete: called when the last reference to a dentry is
|
||||
deleted. This means no-one is using the dentry, however it is
|
||||
still valid and in the dcache
|
||||
d_hash: called when the VFS adds a dentry to the hash table. The first
|
||||
dentry passed to d_hash is the parent directory that the name is
|
||||
to be hashed into. The inode is the dentry's inode.
|
||||
|
||||
Same locking and synchronisation rules as d_compare regarding
|
||||
what is safe to dereference etc.
|
||||
|
||||
d_compare: called to compare a dentry name with a given name. The first
|
||||
dentry is the parent of the dentry to be compared, the second is
|
||||
the parent's inode, then the dentry and inode (may be NULL) of the
|
||||
child dentry. len and name string are properties of the dentry to be
|
||||
compared. qstr is the name to compare it with.
|
||||
|
||||
Must be constant and idempotent, and should not take locks if
|
||||
possible, and should not or store into the dentry or inodes.
|
||||
Should not dereference pointers outside the dentry or inodes without
|
||||
lots of care (eg. d_parent, d_inode, d_name should not be used).
|
||||
|
||||
However, our vfsmount is pinned, and RCU held, so the dentries and
|
||||
inodes won't disappear, neither will our sb or filesystem module.
|
||||
->i_sb and ->d_sb may be used.
|
||||
|
||||
It is a tricky calling convention because it needs to be called under
|
||||
"rcu-walk", ie. without any locks or references on things.
|
||||
|
||||
d_delete: called when the last reference to a dentry is dropped and the
|
||||
dcache is deciding whether or not to cache it. Return 1 to delete
|
||||
immediately, or 0 to cache the dentry. Default is NULL which means to
|
||||
always cache a reachable dentry. d_delete must be constant and
|
||||
idempotent.
|
||||
|
||||
d_release: called when a dentry is really deallocated
|
||||
|
||||
@ -910,14 +953,11 @@ manipulate dentries:
|
||||
the usage count)
|
||||
|
||||
dput: close a handle for a dentry (decrements the usage count). If
|
||||
the usage count drops to 0, the "d_delete" method is called
|
||||
and the dentry is placed on the unused list if the dentry is
|
||||
still in its parents hash list. Putting the dentry on the
|
||||
unused list just means that if the system needs some RAM, it
|
||||
goes through the unused list of dentries and deallocates them.
|
||||
If the dentry has already been unhashed and the usage count
|
||||
drops to 0, in this case the dentry is deallocated after the
|
||||
"d_delete" method is called
|
||||
the usage count drops to 0, and the dentry is still in its
|
||||
parent's hash, the "d_delete" method is called to check whether
|
||||
it should be cached. If it should not be cached, or if the dentry
|
||||
is not hashed, it is deleted. Otherwise cached dentries are put
|
||||
into an LRU list to be reclaimed on memory shortage.
|
||||
|
||||
d_drop: this unhashes a dentry from its parents hash list. A
|
||||
subsequent call to dput() will deallocate the dentry if its
|
||||
|
@ -1542,7 +1542,7 @@ pfm_exit_smpl_buffer(pfm_buffer_fmt_t *fmt)
|
||||
* any operations on the root directory. However, we need a non-trivial
|
||||
* d_name - pfm: will go nicely and kill the special-casing in procfs.
|
||||
*/
|
||||
static struct vfsmount *pfmfs_mnt;
|
||||
static struct vfsmount *pfmfs_mnt __read_mostly;
|
||||
|
||||
static int __init
|
||||
init_pfm_fs(void)
|
||||
@ -2185,7 +2185,7 @@ static const struct file_operations pfm_file_ops = {
|
||||
};
|
||||
|
||||
static int
|
||||
pfmfs_delete_dentry(struct dentry *dentry)
|
||||
pfmfs_delete_dentry(const struct dentry *dentry)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -2233,7 +2233,7 @@ pfm_alloc_file(pfm_context_t *ctx)
|
||||
}
|
||||
path.mnt = mntget(pfmfs_mnt);
|
||||
|
||||
path.dentry->d_op = &pfmfs_dentry_operations;
|
||||
d_set_d_op(path.dentry, &pfmfs_dentry_operations);
|
||||
d_add(path.dentry, inode);
|
||||
|
||||
file = alloc_file(&path, FMODE_READ, &pfm_file_ops);
|
||||
|
@ -71,12 +71,18 @@ spufs_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void
|
||||
spufs_destroy_inode(struct inode *inode)
|
||||
static void spufs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
|
||||
}
|
||||
|
||||
static void spufs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, spufs_i_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
spufs_init_once(void *p)
|
||||
{
|
||||
@ -159,18 +165,18 @@ static void spufs_prune_dir(struct dentry *dir)
|
||||
|
||||
mutex_lock(&dir->d_inode->i_mutex);
|
||||
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(d_unhashed(dentry)) && dentry->d_inode) {
|
||||
dget_locked(dentry);
|
||||
dget_dlock(dentry);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
simple_unlink(dir->d_inode, dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
/* XXX: what was dcache_lock protecting here? Other
|
||||
* filesystems (IB, configfs) release dcache_lock
|
||||
* before unlink */
|
||||
dput(dentry);
|
||||
} else {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
}
|
||||
shrink_dcache_parent(dir);
|
||||
|
@ -277,18 +277,14 @@ static int remove_file(struct dentry *parent, char *name)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&tmp->d_lock);
|
||||
if (!(d_unhashed(tmp) && tmp->d_inode)) {
|
||||
dget_locked(tmp);
|
||||
dget_dlock(tmp);
|
||||
__d_drop(tmp);
|
||||
spin_unlock(&tmp->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
simple_unlink(parent->d_inode, tmp);
|
||||
} else {
|
||||
} else
|
||||
spin_unlock(&tmp->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
bail:
|
||||
|
@ -453,17 +453,14 @@ static int remove_file(struct dentry *parent, char *name)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&tmp->d_lock);
|
||||
if (!(d_unhashed(tmp) && tmp->d_inode)) {
|
||||
dget_locked(tmp);
|
||||
dget_dlock(tmp);
|
||||
__d_drop(tmp);
|
||||
spin_unlock(&tmp->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
simple_unlink(parent->d_inode, tmp);
|
||||
} else {
|
||||
spin_unlock(&tmp->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
@ -1201,7 +1201,7 @@ err_unregister_chdev:
|
||||
static void __exit cleanup_mtdchar(void)
|
||||
{
|
||||
unregister_mtd_user(&mtdchar_notifier);
|
||||
mntput(mtd_inode_mnt);
|
||||
mntput_long(mtd_inode_mnt);
|
||||
unregister_filesystem(&mtd_inodefs_type);
|
||||
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
|
||||
}
|
||||
|
@ -154,13 +154,16 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str
|
||||
* yet completely filled in, and revalidate has to delay such
|
||||
* lookups..
|
||||
*/
|
||||
static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd)
|
||||
static int autofs_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct inode * dir;
|
||||
struct autofs_sb_info *sbi;
|
||||
struct autofs_dir_ent *ent;
|
||||
int res;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
lock_kernel();
|
||||
dir = dentry->d_parent->d_inode;
|
||||
sbi = autofs_sbi(dir->i_sb);
|
||||
@ -237,7 +240,7 @@ static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentr
|
||||
*
|
||||
* We need to do this before we release the directory semaphore.
|
||||
*/
|
||||
dentry->d_op = &autofs_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs_dentry_operations);
|
||||
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
|
||||
d_add(dentry, NULL);
|
||||
|
||||
|
@ -826,6 +826,13 @@ const struct address_space_operations pohmelfs_aops = {
|
||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||
};
|
||||
|
||||
static void pohmelfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(pohmelfs_inode_cache, POHMELFS_I(inode));
|
||||
}
|
||||
|
||||
/*
|
||||
* ->detroy_inode() callback. Deletes inode from the caches
|
||||
* and frees private data.
|
||||
@ -842,8 +849,8 @@ static void pohmelfs_destroy_inode(struct inode *inode)
|
||||
|
||||
dprintk("%s: pi: %p, inode: %p, ino: %llu.\n",
|
||||
__func__, pi, &pi->vfs_inode, pi->ino);
|
||||
kmem_cache_free(pohmelfs_inode_cache, pi);
|
||||
atomic_long_dec(&psb->total_inodes);
|
||||
call_rcu(&inode->i_rcu, pohmelfs_i_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -83,10 +83,11 @@ out:
|
||||
int pohmelfs_path_length(struct pohmelfs_inode *pi)
|
||||
{
|
||||
struct dentry *d, *root, *first;
|
||||
int len = 1; /* Root slash */
|
||||
int len;
|
||||
unsigned seq;
|
||||
|
||||
first = d = d_find_alias(&pi->vfs_inode);
|
||||
if (!d) {
|
||||
first = d_find_alias(&pi->vfs_inode);
|
||||
if (!first) {
|
||||
dprintk("%s: ino: %llu, mode: %o.\n", __func__, pi->ino, pi->vfs_inode.i_mode);
|
||||
return -ENOENT;
|
||||
}
|
||||
@ -95,7 +96,11 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
|
||||
root = dget(current->fs->root.dentry);
|
||||
spin_unlock(¤t->fs->lock);
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
rename_retry:
|
||||
len = 1; /* Root slash */
|
||||
d = first;
|
||||
seq = read_seqbegin(&rename_lock);
|
||||
rcu_read_lock();
|
||||
|
||||
if (!IS_ROOT(d) && d_unhashed(d))
|
||||
len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */
|
||||
@ -104,7 +109,9 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
|
||||
len += d->d_name.len + 1; /* Plus slash */
|
||||
d = d->d_parent;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
rcu_read_unlock();
|
||||
if (read_seqretry(&rename_lock, seq))
|
||||
goto rename_retry;
|
||||
|
||||
dput(root);
|
||||
dput(first);
|
||||
|
@ -62,7 +62,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
|
||||
struct list_head *next;
|
||||
struct dentry *dentry;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
next = parent->d_subdirs.next;
|
||||
while (next != &parent->d_subdirs) {
|
||||
dentry = list_entry(next, struct dentry, d_u.d_child);
|
||||
@ -70,7 +70,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
|
||||
smb_age_dentry(server, dentry);
|
||||
next = next->next;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -96,13 +96,13 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
|
||||
}
|
||||
|
||||
/* If a pointer is invalid, we search the dentry. */
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
next = parent->d_subdirs.next;
|
||||
while (next != &parent->d_subdirs) {
|
||||
dent = list_entry(next, struct dentry, d_u.d_child);
|
||||
if ((unsigned long)dent->d_fsdata == fpos) {
|
||||
if (dent->d_inode)
|
||||
dget_locked(dent);
|
||||
dget(dent);
|
||||
else
|
||||
dent = NULL;
|
||||
goto out_unlock;
|
||||
@ -111,7 +111,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
|
||||
}
|
||||
dent = NULL;
|
||||
out_unlock:
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
return dent;
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
|
||||
qname->hash = full_name_hash(qname->name, qname->len);
|
||||
|
||||
if (dentry->d_op && dentry->d_op->d_hash)
|
||||
if (dentry->d_op->d_hash(dentry, qname) != 0)
|
||||
if (dentry->d_op->d_hash(dentry, inode, qname) != 0)
|
||||
goto end_advance;
|
||||
|
||||
newdent = d_lookup(dentry, qname);
|
||||
@ -145,8 +145,8 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
|
||||
goto end_advance;
|
||||
} else {
|
||||
hashed = 1;
|
||||
memcpy((char *) newdent->d_name.name, qname->name,
|
||||
newdent->d_name.len);
|
||||
/* dir i_mutex is locked because we're in readdir */
|
||||
dentry_update_name_case(newdent, qname);
|
||||
}
|
||||
|
||||
if (!newdent->d_inode) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "smb_fs.h"
|
||||
#include "smb_mount.h"
|
||||
@ -274,9 +275,13 @@ smb_dir_open(struct inode *dir, struct file *file)
|
||||
* Dentry operations routines
|
||||
*/
|
||||
static int smb_lookup_validate(struct dentry *, struct nameidata *);
|
||||
static int smb_hash_dentry(struct dentry *, struct qstr *);
|
||||
static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
static int smb_delete_dentry(struct dentry *);
|
||||
static int smb_hash_dentry(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
static int smb_compare_dentry(const struct dentry *,
|
||||
const struct inode *,
|
||||
const struct dentry *, const struct inode *,
|
||||
unsigned int, const char *, const struct qstr *);
|
||||
static int smb_delete_dentry(const struct dentry *);
|
||||
|
||||
static const struct dentry_operations smbfs_dentry_operations =
|
||||
{
|
||||
@ -297,13 +302,20 @@ static const struct dentry_operations smbfs_dentry_operations_case =
|
||||
* This is the callback when the dcache has a lookup hit.
|
||||
*/
|
||||
static int
|
||||
smb_lookup_validate(struct dentry * dentry, struct nameidata *nd)
|
||||
smb_lookup_validate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct smb_sb_info *server = server_from_dentry(dentry);
|
||||
struct inode * inode = dentry->d_inode;
|
||||
unsigned long age = jiffies - dentry->d_time;
|
||||
struct smb_sb_info *server;
|
||||
struct inode *inode;
|
||||
unsigned long age;
|
||||
int valid;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
server = server_from_dentry(dentry);
|
||||
inode = dentry->d_inode;
|
||||
age = jiffies - dentry->d_time;
|
||||
|
||||
/*
|
||||
* The default validation is based on dentry age:
|
||||
* we believe in dentries for a few seconds. (But each
|
||||
@ -333,7 +345,8 @@ smb_lookup_validate(struct dentry * dentry, struct nameidata *nd)
|
||||
}
|
||||
|
||||
static int
|
||||
smb_hash_dentry(struct dentry *dir, struct qstr *this)
|
||||
smb_hash_dentry(const struct dentry *dir, const struct inode *inode,
|
||||
struct qstr *this)
|
||||
{
|
||||
unsigned long hash;
|
||||
int i;
|
||||
@ -347,14 +360,17 @@ smb_hash_dentry(struct dentry *dir, struct qstr *this)
|
||||
}
|
||||
|
||||
static int
|
||||
smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b)
|
||||
smb_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
int i, result = 1;
|
||||
|
||||
if (a->len != b->len)
|
||||
if (len != name->len)
|
||||
goto out;
|
||||
for (i=0; i < a->len; i++) {
|
||||
if (tolower(a->name[i]) != tolower(b->name[i]))
|
||||
for (i=0; i < len; i++) {
|
||||
if (tolower(str[i]) != tolower(name->name[i]))
|
||||
goto out;
|
||||
}
|
||||
result = 0;
|
||||
@ -367,7 +383,7 @@ out:
|
||||
* We use this to unhash dentries with bad inodes.
|
||||
*/
|
||||
static int
|
||||
smb_delete_dentry(struct dentry * dentry)
|
||||
smb_delete_dentry(const struct dentry *dentry)
|
||||
{
|
||||
if (dentry->d_inode) {
|
||||
if (is_bad_inode(dentry->d_inode)) {
|
||||
@ -390,9 +406,9 @@ smb_new_dentry(struct dentry *dentry)
|
||||
struct smb_sb_info *server = server_from_dentry(dentry);
|
||||
|
||||
if (server->mnt->flags & SMB_MOUNT_CASE)
|
||||
dentry->d_op = &smbfs_dentry_operations_case;
|
||||
d_set_d_op(dentry, &smbfs_dentry_operations_case);
|
||||
else
|
||||
dentry->d_op = &smbfs_dentry_operations;
|
||||
d_set_d_op(dentry, &smbfs_dentry_operations);
|
||||
dentry->d_time = jiffies;
|
||||
}
|
||||
|
||||
@ -454,9 +470,9 @@ smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
add_entry:
|
||||
server = server_from_dentry(dentry);
|
||||
if (server->mnt->flags & SMB_MOUNT_CASE)
|
||||
dentry->d_op = &smbfs_dentry_operations_case;
|
||||
d_set_d_op(dentry, &smbfs_dentry_operations_case);
|
||||
else
|
||||
dentry->d_op = &smbfs_dentry_operations;
|
||||
d_set_d_op(dentry, &smbfs_dentry_operations);
|
||||
|
||||
d_add(dentry, inode);
|
||||
smb_renew_times(dentry);
|
||||
|
@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file)
|
||||
* privileges, so we need our own check for this.
|
||||
*/
|
||||
static int
|
||||
smb_file_permission(struct inode *inode, int mask)
|
||||
smb_file_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
int mode = inode->i_mode;
|
||||
int error = 0;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
VERBOSE("mode=%x, mask=%x\n", mode, mask);
|
||||
|
||||
/* Look at user permissions */
|
||||
|
@ -62,9 +62,16 @@ static struct inode *smb_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void smb_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(smb_inode_cachep, SMB_I(inode));
|
||||
}
|
||||
|
||||
static void smb_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(smb_inode_cachep, SMB_I(inode));
|
||||
call_rcu(&inode->i_rcu, smb_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -343,17 +343,19 @@ static int usbfs_empty (struct dentry *dentry)
|
||||
{
|
||||
struct list_head *list;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each(list, &dentry->d_subdirs) {
|
||||
struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
|
||||
|
||||
spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (usbfs_positive(de)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&de->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&de->d_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
||||
return acl;
|
||||
}
|
||||
|
||||
int v9fs_check_acl(struct inode *inode, int mask)
|
||||
int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
|
||||
/*
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
||||
extern int v9fs_check_acl(struct inode *inode, int mask);
|
||||
extern int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags);
|
||||
extern int v9fs_acl_chmod(struct dentry *);
|
||||
extern int v9fs_set_create_acl(struct dentry *,
|
||||
struct posix_acl *, struct posix_acl *);
|
||||
|
@ -51,7 +51,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_dentry_delete(struct dentry *dentry)
|
||||
static int v9fs_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
|
||||
dentry);
|
||||
@ -68,7 +68,7 @@ static int v9fs_dentry_delete(struct dentry *dentry)
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_cached_dentry_delete(struct dentry *dentry)
|
||||
static int v9fs_cached_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
|
||||
|
@ -237,9 +237,16 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
|
||||
}
|
||||
|
||||
void v9fs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
|
||||
call_rcu(&inode->i_rcu, v9fs_i_callback);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -270,11 +277,11 @@ static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
/* Directory should have only one entry. */
|
||||
BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
|
||||
dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
@ -628,9 +635,9 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||
}
|
||||
|
||||
if (v9ses->cache)
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
else
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
@ -742,7 +749,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
@ -760,7 +767,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
@ -949,7 +956,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
@ -966,7 +973,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
@ -1034,9 +1041,9 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
inst_out:
|
||||
if (v9ses->cache)
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
else
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
@ -1702,7 +1709,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
@ -1715,7 +1722,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
|
||||
@ -1849,7 +1856,7 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
|
||||
ihold(old_dentry->d_inode);
|
||||
}
|
||||
|
||||
dentry->d_op = old_dentry->d_op;
|
||||
d_set_d_op(dentry, old_dentry->d_op);
|
||||
d_instantiate(dentry, old_dentry->d_inode);
|
||||
|
||||
return err;
|
||||
@ -1973,7 +1980,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_cached_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
@ -1989,7 +1996,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_set_d_op(dentry, &v9fs_dentry_operations);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
|
@ -201,7 +201,8 @@ const struct file_operations adfs_dir_operations = {
|
||||
};
|
||||
|
||||
static int
|
||||
adfs_hash(struct dentry *parent, struct qstr *qstr)
|
||||
adfs_hash(const struct dentry *parent, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
|
||||
const unsigned char *name;
|
||||
@ -237,17 +238,19 @@ adfs_hash(struct dentry *parent, struct qstr *qstr)
|
||||
* requirements of the underlying filesystem.
|
||||
*/
|
||||
static int
|
||||
adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
|
||||
adfs_compare(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (entry->len != name->len)
|
||||
if (len != name->len)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < name->len; i++) {
|
||||
char a, b;
|
||||
|
||||
a = entry->name[i];
|
||||
a = str[i];
|
||||
b = name->name[i];
|
||||
|
||||
if (a >= 'A' && a <= 'Z')
|
||||
@ -273,7 +276,7 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
struct object_info obj;
|
||||
int error;
|
||||
|
||||
dentry->d_op = &adfs_dentry_operations;
|
||||
d_set_d_op(dentry, &adfs_dentry_operations);
|
||||
lock_kernel();
|
||||
error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
|
||||
if (error == 0) {
|
||||
|
@ -240,9 +240,16 @@ static struct inode *adfs_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void adfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
|
||||
}
|
||||
|
||||
static void adfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
|
||||
call_rcu(&inode->i_rcu, adfs_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
@ -477,7 +484,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
adfs_error(sb, "get root inode failed\n");
|
||||
goto error;
|
||||
} else
|
||||
sb->s_root->d_op = &adfs_dentry_operations;
|
||||
d_set_d_op(sb->s_root, &adfs_dentry_operations);
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
|
||||
|
@ -128,7 +128,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
|
||||
void *data = dentry->d_fsdata;
|
||||
struct list_head *head, *next;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
head = &inode->i_dentry;
|
||||
next = head->next;
|
||||
while (next != head) {
|
||||
@ -139,7 +139,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
|
||||
}
|
||||
next = next->next;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,11 +13,19 @@
|
||||
typedef int (*toupper_t)(int);
|
||||
|
||||
static int affs_toupper(int ch);
|
||||
static int affs_hash_dentry(struct dentry *, struct qstr *);
|
||||
static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
static int affs_hash_dentry(const struct dentry *,
|
||||
const struct inode *, struct qstr *);
|
||||
static int affs_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
static int affs_intl_toupper(int ch);
|
||||
static int affs_intl_hash_dentry(struct dentry *, struct qstr *);
|
||||
static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
static int affs_intl_hash_dentry(const struct dentry *,
|
||||
const struct inode *, struct qstr *);
|
||||
static int affs_intl_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
const struct dentry_operations affs_dentry_operations = {
|
||||
.d_hash = affs_hash_dentry,
|
||||
@ -58,13 +66,13 @@ affs_get_toupper(struct super_block *sb)
|
||||
* Note: the dentry argument is the parent dentry.
|
||||
*/
|
||||
static inline int
|
||||
__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
|
||||
__affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
|
||||
{
|
||||
const u8 *name = qstr->name;
|
||||
unsigned long hash;
|
||||
int i;
|
||||
|
||||
i = affs_check_name(qstr->name,qstr->len);
|
||||
i = affs_check_name(qstr->name, qstr->len);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
@ -78,39 +86,41 @@ __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
|
||||
}
|
||||
|
||||
static int
|
||||
affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
|
||||
affs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(dentry, qstr, affs_toupper);
|
||||
return __affs_hash_dentry(qstr, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
|
||||
affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
|
||||
return __affs_hash_dentry(qstr, affs_intl_toupper);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
|
||||
static inline int __affs_compare_dentry(unsigned int len,
|
||||
const char *str, const struct qstr *name, toupper_t toupper)
|
||||
{
|
||||
const u8 *aname = a->name;
|
||||
const u8 *bname = b->name;
|
||||
int len;
|
||||
const u8 *aname = str;
|
||||
const u8 *bname = name->name;
|
||||
|
||||
/* 'a' is the qstr of an already existing dentry, so the name
|
||||
* must be valid. 'b' must be validated first.
|
||||
/*
|
||||
* 'str' is the name of an already existing dentry, so the name
|
||||
* must be valid. 'name' must be validated first.
|
||||
*/
|
||||
|
||||
if (affs_check_name(b->name,b->len))
|
||||
if (affs_check_name(name->name, name->len))
|
||||
return 1;
|
||||
|
||||
/* If the names are longer than the allowed 30 chars,
|
||||
/*
|
||||
* If the names are longer than the allowed 30 chars,
|
||||
* the excess is ignored, so their length may differ.
|
||||
*/
|
||||
len = a->len;
|
||||
if (len >= 30) {
|
||||
if (b->len < 30)
|
||||
if (name->len < 30)
|
||||
return 1;
|
||||
len = 30;
|
||||
} else if (len != b->len)
|
||||
} else if (len != name->len)
|
||||
return 1;
|
||||
|
||||
for (; len > 0; len--)
|
||||
@ -121,14 +131,18 @@ __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, tou
|
||||
}
|
||||
|
||||
static int
|
||||
affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
affs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return __affs_compare_dentry(dentry, a, b, affs_toupper);
|
||||
return __affs_compare_dentry(len, str, name, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
|
||||
return __affs_compare_dentry(len, str, name, affs_intl_toupper);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -226,7 +240,7 @@ affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
|
||||
d_set_d_op(dentry, AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations);
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -95,9 +95,16 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
|
||||
return &i->vfs_inode;
|
||||
}
|
||||
|
||||
static void affs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
|
||||
}
|
||||
|
||||
static void affs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
|
||||
call_rcu(&inode->i_rcu, affs_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
@ -475,7 +482,7 @@ got_root:
|
||||
printk(KERN_ERR "AFFS: Get root inode failed\n");
|
||||
goto out_error;
|
||||
}
|
||||
sb->s_root->d_op = &affs_dentry_operations;
|
||||
d_set_d_op(sb->s_root, &affs_dentry_operations);
|
||||
|
||||
pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
|
||||
return 0;
|
||||
|
10
fs/afs/dir.c
10
fs/afs/dir.c
@ -13,6 +13,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/sched.h>
|
||||
@ -23,7 +24,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
static int afs_dir_open(struct inode *inode, struct file *file);
|
||||
static int afs_readdir(struct file *file, void *dirent, filldir_t filldir);
|
||||
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
|
||||
static int afs_d_delete(struct dentry *dentry);
|
||||
static int afs_d_delete(const struct dentry *dentry);
|
||||
static void afs_d_release(struct dentry *dentry);
|
||||
static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
|
||||
loff_t fpos, u64 ino, unsigned dtype);
|
||||
@ -581,7 +582,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
|
||||
success:
|
||||
dentry->d_op = &afs_fs_dentry_operations;
|
||||
d_set_d_op(dentry, &afs_fs_dentry_operations);
|
||||
|
||||
d_add(dentry, inode);
|
||||
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%llu }",
|
||||
@ -607,6 +608,9 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
void *dir_version;
|
||||
int ret;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
vnode = AFS_FS_I(dentry->d_inode);
|
||||
|
||||
if (dentry->d_inode)
|
||||
@ -730,7 +734,7 @@ out_bad:
|
||||
* - called from dput() when d_count is going to 0.
|
||||
* - return 1 to request dentry be unhashed, 0 otherwise
|
||||
*/
|
||||
static int afs_d_delete(struct dentry *dentry)
|
||||
static int afs_d_delete(const struct dentry *dentry)
|
||||
{
|
||||
_enter("%s", dentry->d_name.name);
|
||||
|
||||
|
@ -624,7 +624,7 @@ extern void afs_clear_permits(struct afs_vnode *);
|
||||
extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
|
||||
extern void afs_zap_permits(struct rcu_head *);
|
||||
extern struct key *afs_request_key(struct afs_cell *);
|
||||
extern int afs_permission(struct inode *, int);
|
||||
extern int afs_permission(struct inode *, int, unsigned int);
|
||||
|
||||
/*
|
||||
* server.c
|
||||
|
@ -285,13 +285,16 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
|
||||
* - AFS ACLs are attached to directories only, and a file is controlled by its
|
||||
* parent directory's ACL
|
||||
*/
|
||||
int afs_permission(struct inode *inode, int mask)
|
||||
int afs_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
afs_access_t uninitialized_var(access);
|
||||
struct key *key;
|
||||
int ret;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
_enter("{{%x:%u},%lx},%x,",
|
||||
vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
|
||||
|
||||
@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask)
|
||||
}
|
||||
|
||||
key_put(key);
|
||||
ret = generic_permission(inode, mask, NULL);
|
||||
ret = generic_permission(inode, mask, flags, NULL);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
||||
|
@ -498,6 +498,14 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
|
||||
return &vnode->vfs_inode;
|
||||
}
|
||||
|
||||
static void afs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(afs_inode_cachep, vnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* destroy an AFS inode struct
|
||||
*/
|
||||
@ -511,7 +519,7 @@ static void afs_destroy_inode(struct inode *inode)
|
||||
|
||||
ASSERTCMP(vnode->server, ==, NULL);
|
||||
|
||||
kmem_cache_free(afs_inode_cachep, vnode);
|
||||
call_rcu(&inode->i_rcu, afs_i_callback);
|
||||
atomic_dec(&afs_count_active_inodes);
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ struct file *anon_inode_getfile(const char *name,
|
||||
this.name = name;
|
||||
this.len = strlen(name);
|
||||
this.hash = 0;
|
||||
path.dentry = d_alloc(anon_inode_mnt->mnt_sb->s_root, &this);
|
||||
path.dentry = d_alloc_pseudo(anon_inode_mnt->mnt_sb, &this);
|
||||
if (!path.dentry)
|
||||
goto err_module;
|
||||
|
||||
@ -113,7 +113,7 @@ struct file *anon_inode_getfile(const char *name,
|
||||
*/
|
||||
ihold(anon_inode_inode);
|
||||
|
||||
path.dentry->d_op = &anon_inodefs_dentry_operations;
|
||||
d_set_d_op(path.dentry, &anon_inodefs_dentry_operations);
|
||||
d_instantiate(path.dentry, anon_inode_inode);
|
||||
|
||||
error = -ENFILE;
|
||||
@ -232,7 +232,7 @@ static int __init anon_inode_init(void)
|
||||
return 0;
|
||||
|
||||
err_mntput:
|
||||
mntput(anon_inode_mnt);
|
||||
mntput_long(anon_inode_mnt);
|
||||
err_unregister_filesystem:
|
||||
unregister_filesystem(&anon_inode_fs_type);
|
||||
err_exit:
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/auto_fs4.h>
|
||||
#include <linux/auto_dev-ioctl.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* This is the range of ioctl() numbers we claim as ours */
|
||||
@ -60,6 +61,8 @@ do { \
|
||||
current->pid, __func__, ##args); \
|
||||
} while (0)
|
||||
|
||||
extern spinlock_t autofs4_lock;
|
||||
|
||||
/* Unified info structure. This is pointed to by both the dentry and
|
||||
inode structures. Each file in the filesystem has an instance of this
|
||||
structure. It holds a reference to the dentry, so dentries are never
|
||||
@ -254,17 +257,15 @@ static inline int simple_positive(struct dentry *dentry)
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
static inline int __simple_empty(struct dentry *dentry)
|
||||
static inline void __autofs4_add_expiring(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *child;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child)
|
||||
if (simple_positive(child))
|
||||
goto out;
|
||||
ret = 1;
|
||||
out:
|
||||
return ret;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
if (ino) {
|
||||
if (list_empty(&ino->expiring))
|
||||
list_add(&ino->expiring, &sbi->expiring_list);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void autofs4_add_expiring(struct dentry *dentry)
|
||||
|
@ -91,24 +91,64 @@ done:
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate next entry in top down tree traversal.
|
||||
* From next_mnt in namespace.c - elegant.
|
||||
* Calculate and dget next entry in top down tree traversal.
|
||||
*/
|
||||
static struct dentry *next_dentry(struct dentry *p, struct dentry *root)
|
||||
static struct dentry *get_next_positive_dentry(struct dentry *prev,
|
||||
struct dentry *root)
|
||||
{
|
||||
struct list_head *next = p->d_subdirs.next;
|
||||
struct list_head *next;
|
||||
struct dentry *p, *ret;
|
||||
|
||||
if (prev == NULL)
|
||||
return dget(prev);
|
||||
|
||||
spin_lock(&autofs4_lock);
|
||||
relock:
|
||||
p = prev;
|
||||
spin_lock(&p->d_lock);
|
||||
again:
|
||||
next = p->d_subdirs.next;
|
||||
if (next == &p->d_subdirs) {
|
||||
while (1) {
|
||||
if (p == root)
|
||||
struct dentry *parent;
|
||||
|
||||
if (p == root) {
|
||||
spin_unlock(&p->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
dput(prev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent = p->d_parent;
|
||||
if (!spin_trylock(&parent->d_lock)) {
|
||||
spin_unlock(&p->d_lock);
|
||||
cpu_relax();
|
||||
goto relock;
|
||||
}
|
||||
spin_unlock(&p->d_lock);
|
||||
next = p->d_u.d_child.next;
|
||||
if (next != &p->d_parent->d_subdirs)
|
||||
p = parent;
|
||||
if (next != &parent->d_subdirs)
|
||||
break;
|
||||
p = p->d_parent;
|
||||
}
|
||||
}
|
||||
return list_entry(next, struct dentry, d_u.d_child);
|
||||
ret = list_entry(next, struct dentry, d_u.d_child);
|
||||
|
||||
spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
/* Negative dentry - try next */
|
||||
if (!simple_positive(ret)) {
|
||||
spin_unlock(&ret->d_lock);
|
||||
p = ret;
|
||||
goto again;
|
||||
}
|
||||
dget_dlock(ret);
|
||||
spin_unlock(&ret->d_lock);
|
||||
spin_unlock(&p->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
dput(prev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -158,18 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
|
||||
if (!simple_positive(top))
|
||||
return 1;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
for (p = top; p; p = next_dentry(p, top)) {
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(p))
|
||||
continue;
|
||||
|
||||
p = NULL;
|
||||
while ((p = get_next_positive_dentry(p, top))) {
|
||||
DPRINTK("dentry %p %.*s",
|
||||
p, (int) p->d_name.len, p->d_name.name);
|
||||
|
||||
p = dget(p);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
/*
|
||||
* Is someone visiting anywhere in the subtree ?
|
||||
* If there's no mount we need to check the usage
|
||||
@ -198,16 +231,13 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
|
||||
else
|
||||
ino_count++;
|
||||
|
||||
if (atomic_read(&p->d_count) > ino_count) {
|
||||
if (p->d_count > ino_count) {
|
||||
top_ino->last_used = jiffies;
|
||||
dput(p);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
dput(p);
|
||||
spin_lock(&dcache_lock);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
/* Timeout of a tree mount is ultimately determined by its top dentry */
|
||||
if (!autofs4_can_expire(top, timeout, do_now))
|
||||
@ -226,32 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
|
||||
DPRINTK("parent %p %.*s",
|
||||
parent, (int)parent->d_name.len, parent->d_name.name);
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
for (p = parent; p; p = next_dentry(p, parent)) {
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(p))
|
||||
continue;
|
||||
|
||||
p = NULL;
|
||||
while ((p = get_next_positive_dentry(p, parent))) {
|
||||
DPRINTK("dentry %p %.*s",
|
||||
p, (int) p->d_name.len, p->d_name.name);
|
||||
|
||||
p = dget(p);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
if (d_mountpoint(p)) {
|
||||
/* Can we umount this guy */
|
||||
if (autofs4_mount_busy(mnt, p))
|
||||
goto cont;
|
||||
continue;
|
||||
|
||||
/* Can we expire this guy */
|
||||
if (autofs4_can_expire(p, timeout, do_now))
|
||||
return p;
|
||||
}
|
||||
cont:
|
||||
dput(p);
|
||||
spin_lock(&dcache_lock);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -276,7 +295,9 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
|
||||
struct autofs_info *ino = autofs4_dentry_ino(root);
|
||||
if (d_mountpoint(root)) {
|
||||
ino->flags |= AUTOFS_INF_MOUNTPOINT;
|
||||
root->d_mounted--;
|
||||
spin_lock(&root->d_lock);
|
||||
root->d_flags &= ~DCACHE_MOUNTED;
|
||||
spin_unlock(&root->d_lock);
|
||||
}
|
||||
ino->flags |= AUTOFS_INF_EXPIRING;
|
||||
init_completion(&ino->expire_complete);
|
||||
@ -302,8 +323,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct dentry *root = sb->s_root;
|
||||
struct dentry *dentry;
|
||||
struct dentry *expired = NULL;
|
||||
struct list_head *next;
|
||||
int do_now = how & AUTOFS_EXP_IMMEDIATE;
|
||||
int exp_leaves = how & AUTOFS_EXP_LEAVES;
|
||||
struct autofs_info *ino;
|
||||
@ -315,23 +336,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
now = jiffies;
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
next = root->d_subdirs.next;
|
||||
|
||||
/* On exit from the loop expire is set to a dgot dentry
|
||||
* to expire or it's NULL */
|
||||
while ( next != &root->d_subdirs ) {
|
||||
struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
|
||||
|
||||
/* Negative dentry - give up */
|
||||
if (!simple_positive(dentry)) {
|
||||
next = next->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
dentry = dget(dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
dentry = NULL;
|
||||
while ((dentry = get_next_positive_dentry(dentry, root))) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs4_dentry_ino(dentry);
|
||||
|
||||
@ -347,7 +353,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
|
||||
/* Path walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 2;
|
||||
if (atomic_read(&dentry->d_count) > ino_count)
|
||||
if (dentry->d_count > ino_count)
|
||||
goto next;
|
||||
|
||||
/* Can we umount this guy */
|
||||
@ -369,7 +375,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
if (!exp_leaves) {
|
||||
/* Path walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (atomic_read(&dentry->d_count) > ino_count)
|
||||
if (dentry->d_count > ino_count)
|
||||
goto next;
|
||||
|
||||
if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
|
||||
@ -383,7 +389,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
} else {
|
||||
/* Path walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (atomic_read(&dentry->d_count) > ino_count)
|
||||
if (dentry->d_count > ino_count)
|
||||
goto next;
|
||||
|
||||
expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
|
||||
@ -394,11 +400,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
}
|
||||
next:
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
dput(dentry);
|
||||
spin_lock(&dcache_lock);
|
||||
next = next->next;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
@ -408,9 +410,13 @@ found:
|
||||
ino->flags |= AUTOFS_INF_EXPIRING;
|
||||
init_completion(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&expired->d_parent->d_lock);
|
||||
spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&expired->d_lock);
|
||||
spin_unlock(&expired->d_parent->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
return expired;
|
||||
}
|
||||
|
||||
@ -499,7 +505,14 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
|
||||
sb->s_root->d_mounted++;
|
||||
spin_lock(&sb->s_root->d_lock);
|
||||
/*
|
||||
* If we haven't been expired away, then reset
|
||||
* mounted status.
|
||||
*/
|
||||
if (mnt->mnt_parent != mnt)
|
||||
sb->s_root->d_flags |= DCACHE_MOUNTED;
|
||||
spin_unlock(&sb->s_root->d_lock);
|
||||
ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
|
||||
}
|
||||
ino->flags &= ~AUTOFS_INF_EXPIRING;
|
||||
|
@ -309,7 +309,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
goto fail_iput;
|
||||
pipe = NULL;
|
||||
|
||||
root->d_op = &autofs4_sb_dentry_operations;
|
||||
d_set_d_op(root, &autofs4_sb_dentry_operations);
|
||||
root->d_fsdata = ino;
|
||||
|
||||
/* Can this call block? */
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "autofs_i.h"
|
||||
|
||||
DEFINE_SPINLOCK(autofs4_lock);
|
||||
|
||||
static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
|
||||
static int autofs4_dir_unlink(struct inode *,struct dentry *);
|
||||
static int autofs4_dir_rmdir(struct inode *,struct dentry *);
|
||||
@ -142,12 +144,15 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
|
||||
* autofs file system so just let the libfs routines handle
|
||||
* it.
|
||||
*/
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
out:
|
||||
return dcache_dir_open(inode, file);
|
||||
@ -252,9 +257,11 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
/* We trigger a mount for almost all flags */
|
||||
lookup_type = autofs4_need_mount(nd->flags);
|
||||
spin_lock(&sbi->fs_lock);
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
goto follow;
|
||||
}
|
||||
@ -266,7 +273,8 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
*/
|
||||
if (ino->flags & AUTOFS_INF_PENDING ||
|
||||
(!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
status = try_to_fill_dentry(dentry, nd->flags);
|
||||
@ -275,7 +283,8 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
|
||||
goto follow;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
follow:
|
||||
/*
|
||||
@ -306,12 +315,19 @@ out_error:
|
||||
*/
|
||||
static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct inode *dir = dentry->d_parent->d_inode;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
int oz_mode = autofs4_oz_mode(sbi);
|
||||
struct inode *dir;
|
||||
struct autofs_sb_info *sbi;
|
||||
int oz_mode;
|
||||
int flags = nd ? nd->flags : 0;
|
||||
int status = 1;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dentry->d_parent->d_inode;
|
||||
sbi = autofs4_sbi(dir->i_sb);
|
||||
oz_mode = autofs4_oz_mode(sbi);
|
||||
|
||||
/* Pending dentry */
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (autofs4_ispending(dentry)) {
|
||||
@ -346,12 +362,14 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
return 0;
|
||||
|
||||
/* Check for a non-mountpoint directory with no contents */
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (S_ISDIR(dentry->d_inode->i_mode) &&
|
||||
!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
|
||||
DPRINTK("dentry=%p %.*s, emptydir",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
/* The daemon never causes a mount to trigger */
|
||||
if (oz_mode)
|
||||
@ -367,7 +385,8 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
|
||||
return status;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -422,7 +441,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
|
||||
const unsigned char *str = name->name;
|
||||
struct list_head *p, *head;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&sbi->lookup_lock);
|
||||
head = &sbi->active_list;
|
||||
list_for_each(p, head) {
|
||||
@ -436,7 +455,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
|
||||
spin_lock(&active->d_lock);
|
||||
|
||||
/* Already gone? */
|
||||
if (atomic_read(&active->d_count) == 0)
|
||||
if (active->d_count == 0)
|
||||
goto next;
|
||||
|
||||
qstr = &active->d_name;
|
||||
@ -452,17 +471,17 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
|
||||
goto next;
|
||||
|
||||
if (d_unhashed(active)) {
|
||||
dget(active);
|
||||
dget_dlock(active);
|
||||
spin_unlock(&active->d_lock);
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
return active;
|
||||
}
|
||||
next:
|
||||
spin_unlock(&active->d_lock);
|
||||
}
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -477,7 +496,7 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
|
||||
const unsigned char *str = name->name;
|
||||
struct list_head *p, *head;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&sbi->lookup_lock);
|
||||
head = &sbi->expiring_list;
|
||||
list_for_each(p, head) {
|
||||
@ -507,17 +526,17 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
|
||||
goto next;
|
||||
|
||||
if (d_unhashed(expiring)) {
|
||||
dget(expiring);
|
||||
dget_dlock(expiring);
|
||||
spin_unlock(&expiring->d_lock);
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
return expiring;
|
||||
}
|
||||
next:
|
||||
spin_unlock(&expiring->d_lock);
|
||||
}
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -559,7 +578,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
|
||||
* we check for the hashed dentry and return the newly
|
||||
* hashed dentry.
|
||||
*/
|
||||
dentry->d_op = &autofs4_root_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
|
||||
/*
|
||||
* And we need to ensure that the same dentry is used for
|
||||
@ -698,9 +717,9 @@ static int autofs4_dir_symlink(struct inode *dir,
|
||||
d_add(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
dentry->d_op = &autofs4_root_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
else
|
||||
dentry->d_op = &autofs4_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs4_dentry_operations);
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
@ -753,12 +772,12 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
autofs4_add_expiring(dentry);
|
||||
spin_lock(&dentry->d_lock);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -775,16 +794,20 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
if (!autofs4_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&sbi->lookup_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
autofs4_add_expiring(dentry);
|
||||
spin_lock(&dentry->d_lock);
|
||||
__autofs4_add_expiring(dentry);
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
if (atomic_dec_and_test(&ino->count)) {
|
||||
p_ino = autofs4_dentry_ino(dentry->d_parent);
|
||||
@ -829,9 +852,9 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
d_add(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
dentry->d_op = &autofs4_root_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
else
|
||||
dentry->d_op = &autofs4_dentry_operations;
|
||||
d_set_d_op(dentry, &autofs4_dentry_operations);
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
|
@ -186,16 +186,26 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
|
||||
{
|
||||
struct dentry *root = sbi->sb->s_root;
|
||||
struct dentry *tmp;
|
||||
char *buf = *name;
|
||||
char *buf;
|
||||
char *p;
|
||||
int len = 0;
|
||||
int len;
|
||||
unsigned seq;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
rename_retry:
|
||||
buf = *name;
|
||||
len = 0;
|
||||
|
||||
seq = read_seqbegin(&rename_lock);
|
||||
rcu_read_lock();
|
||||
spin_lock(&autofs4_lock);
|
||||
for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
|
||||
len += tmp->d_name.len + 1;
|
||||
|
||||
if (!len || --len > NAME_MAX) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
rcu_read_unlock();
|
||||
if (read_seqretry(&rename_lock, seq))
|
||||
goto rename_retry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -208,7 +218,10 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
|
||||
p -= tmp->d_name.len;
|
||||
strncpy(p, tmp->d_name.name, tmp->d_name.len);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
rcu_read_unlock();
|
||||
if (read_seqretry(&rename_lock, seq))
|
||||
goto rename_retry;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int bad_inode_permission(struct inode *inode, int mask)
|
||||
static int bad_inode_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -284,12 +284,18 @@ befs_alloc_inode(struct super_block *sb)
|
||||
return &bi->vfs_inode;
|
||||
}
|
||||
|
||||
static void
|
||||
befs_destroy_inode(struct inode *inode)
|
||||
static void befs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(befs_inode_cachep, BEFS_I(inode));
|
||||
}
|
||||
|
||||
static void befs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, befs_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
{
|
||||
struct befs_inode_info *bi = (struct befs_inode_info *) foo;
|
||||
|
@ -248,9 +248,16 @@ static struct inode *bfs_alloc_inode(struct super_block *sb)
|
||||
return &bi->vfs_inode;
|
||||
}
|
||||
|
||||
static void bfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
|
||||
}
|
||||
|
||||
static void bfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
|
||||
call_rcu(&inode->i_rcu, bfs_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -409,13 +409,20 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void bdev_destroy_inode(struct inode *inode)
|
||||
static void bdev_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
struct bdev_inode *bdi = BDEV_I(inode);
|
||||
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(bdev_cachep, bdi);
|
||||
}
|
||||
|
||||
static void bdev_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, bdev_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
{
|
||||
struct bdev_inode *ei = (struct bdev_inode *) foo;
|
||||
|
@ -185,18 +185,23 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_check_acl(struct inode *inode, int mask)
|
||||
int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int error = -EAGAIN;
|
||||
|
||||
acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (flags & IPERM_FLAG_RCU) {
|
||||
if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
|
||||
error = -ECHILD;
|
||||
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
error = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
} else {
|
||||
struct posix_acl *acl;
|
||||
acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
error = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait);
|
||||
|
||||
/* acl.c */
|
||||
#ifdef CONFIG_BTRFS_FS_POSIX_ACL
|
||||
int btrfs_check_acl(struct inode *inode, int mask);
|
||||
int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags);
|
||||
#else
|
||||
#define btrfs_check_acl NULL
|
||||
#endif
|
||||
|
@ -110,7 +110,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
|
||||
|
||||
dentry = d_obtain_alias(inode);
|
||||
if (!IS_ERR(dentry))
|
||||
dentry->d_op = &btrfs_dentry_operations;
|
||||
d_set_d_op(dentry, &btrfs_dentry_operations);
|
||||
return dentry;
|
||||
fail:
|
||||
srcu_read_unlock(&fs_info->subvol_srcu, index);
|
||||
@ -225,7 +225,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
|
||||
key.offset = 0;
|
||||
dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));
|
||||
if (!IS_ERR(dentry))
|
||||
dentry->d_op = &btrfs_dentry_operations;
|
||||
d_set_d_op(dentry, &btrfs_dentry_operations);
|
||||
return dentry;
|
||||
fail:
|
||||
btrfs_free_path(path);
|
||||
|
@ -4084,7 +4084,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
||||
int index;
|
||||
int ret;
|
||||
|
||||
dentry->d_op = &btrfs_dentry_operations;
|
||||
d_set_d_op(dentry, &btrfs_dentry_operations);
|
||||
|
||||
if (dentry->d_name.len > BTRFS_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
@ -4127,7 +4127,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
||||
return inode;
|
||||
}
|
||||
|
||||
static int btrfs_dentry_delete(struct dentry *dentry)
|
||||
static int btrfs_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
struct btrfs_root *root;
|
||||
|
||||
@ -6495,6 +6495,13 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void btrfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
|
||||
}
|
||||
|
||||
void btrfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
@ -6564,7 +6571,7 @@ void btrfs_destroy_inode(struct inode *inode)
|
||||
inode_tree_del(inode);
|
||||
btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
|
||||
free:
|
||||
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
|
||||
call_rcu(&inode->i_rcu, btrfs_i_callback);
|
||||
}
|
||||
|
||||
int btrfs_drop_inode(struct inode *inode)
|
||||
@ -7204,11 +7211,11 @@ static int btrfs_set_page_dirty(struct page *page)
|
||||
return __set_page_dirty_nobuffers(page);
|
||||
}
|
||||
|
||||
static int btrfs_permission(struct inode *inode, int mask)
|
||||
static int btrfs_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
|
||||
return -EACCES;
|
||||
return generic_permission(inode, mask, btrfs_check_acl);
|
||||
return generic_permission(inode, mask, flags, btrfs_check_acl);
|
||||
}
|
||||
|
||||
static const struct inode_operations btrfs_dir_inode_operations = {
|
||||
|
@ -42,11 +42,11 @@ int ceph_init_dentry(struct dentry *dentry)
|
||||
|
||||
if (dentry->d_parent == NULL || /* nfs fh_to_dentry */
|
||||
ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
|
||||
dentry->d_op = &ceph_dentry_ops;
|
||||
d_set_d_op(dentry, &ceph_dentry_ops);
|
||||
else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
|
||||
dentry->d_op = &ceph_snapdir_dentry_ops;
|
||||
d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
|
||||
else
|
||||
dentry->d_op = &ceph_snap_dentry_ops;
|
||||
d_set_d_op(dentry, &ceph_snap_dentry_ops);
|
||||
|
||||
di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
|
||||
if (!di)
|
||||
@ -112,7 +112,7 @@ static int __dcache_readdir(struct file *filp,
|
||||
dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos,
|
||||
last);
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
|
||||
/* start at beginning? */
|
||||
if (filp->f_pos == 2 || last == NULL ||
|
||||
@ -136,6 +136,7 @@ more:
|
||||
fi->at_end = 1;
|
||||
goto out_unlock;
|
||||
}
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (!d_unhashed(dentry) && dentry->d_inode &&
|
||||
ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
|
||||
ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
|
||||
@ -145,13 +146,15 @@ more:
|
||||
dentry->d_name.len, dentry->d_name.name, di->offset,
|
||||
filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",
|
||||
!dentry->d_inode ? " null" : "");
|
||||
spin_unlock(&dentry->d_lock);
|
||||
p = p->prev;
|
||||
dentry = list_entry(p, struct dentry, d_u.d_child);
|
||||
di = ceph_dentry(dentry);
|
||||
}
|
||||
|
||||
atomic_inc(&dentry->d_count);
|
||||
spin_unlock(&dcache_lock);
|
||||
dget_dlock(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
|
||||
dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,
|
||||
dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
||||
@ -177,19 +180,19 @@ more:
|
||||
|
||||
filp->f_pos++;
|
||||
|
||||
/* make sure a dentry wasn't dropped while we didn't have dcache_lock */
|
||||
/* make sure a dentry wasn't dropped while we didn't have parent lock */
|
||||
if (!ceph_i_test(dir, CEPH_I_COMPLETE)) {
|
||||
dout(" lost I_COMPLETE on %p; falling back to mds\n", dir);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
p = p->prev; /* advance to next dentry */
|
||||
goto more;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
out:
|
||||
if (last)
|
||||
dput(last);
|
||||
@ -987,7 +990,12 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
|
||||
*/
|
||||
static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct inode *dir = dentry->d_parent->d_inode;
|
||||
struct inode *dir;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dentry->d_parent->d_inode;
|
||||
|
||||
dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
|
||||
dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
|
||||
|
@ -368,6 +368,15 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
|
||||
return &ci->vfs_inode;
|
||||
}
|
||||
|
||||
static void ceph_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(ceph_inode_cachep, ci);
|
||||
}
|
||||
|
||||
void ceph_destroy_inode(struct inode *inode)
|
||||
{
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
@ -407,7 +416,7 @@ void ceph_destroy_inode(struct inode *inode)
|
||||
if (ci->i_xattrs.prealloc_blob)
|
||||
ceph_buffer_put(ci->i_xattrs.prealloc_blob);
|
||||
|
||||
kmem_cache_free(ceph_inode_cachep, ci);
|
||||
call_rcu(&inode->i_rcu, ceph_i_callback);
|
||||
}
|
||||
|
||||
|
||||
@ -841,13 +850,13 @@ static void ceph_set_dentry_offset(struct dentry *dn)
|
||||
di->offset = ceph_inode(inode)->i_max_offset++;
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dn->d_lock);
|
||||
spin_lock(&dir->d_lock);
|
||||
spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
list_move(&dn->d_u.d_child, &dir->d_subdirs);
|
||||
dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset,
|
||||
dn->d_u.d_child.prev, dn->d_u.d_child.next);
|
||||
spin_unlock(&dn->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dir->d_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -879,8 +888,8 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in,
|
||||
} else if (realdn) {
|
||||
dout("dn %p (%d) spliced with %p (%d) "
|
||||
"inode %p ino %llx.%llx\n",
|
||||
dn, atomic_read(&dn->d_count),
|
||||
realdn, atomic_read(&realdn->d_count),
|
||||
dn, dn->d_count,
|
||||
realdn, realdn->d_count,
|
||||
realdn->d_inode, ceph_vinop(realdn->d_inode));
|
||||
dput(dn);
|
||||
dn = realdn;
|
||||
@ -1231,11 +1240,11 @@ retry_lookup:
|
||||
goto retry_lookup;
|
||||
} else {
|
||||
/* reorder parent's d_subdirs */
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dn->d_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
list_move(&dn->d_u.d_child, &parent->d_subdirs);
|
||||
spin_unlock(&dn->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
}
|
||||
|
||||
di = dn->d_fsdata;
|
||||
@ -1772,12 +1781,17 @@ int ceph_do_getattr(struct inode *inode, int mask)
|
||||
* Check inode permissions. We verify we have a valid value for
|
||||
* the AUTH cap, then call the generic handler.
|
||||
*/
|
||||
int ceph_permission(struct inode *inode, int mask)
|
||||
int ceph_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
int err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
|
||||
int err;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
|
||||
|
||||
if (!err)
|
||||
err = generic_permission(inode, mask, NULL);
|
||||
err = generic_permission(inode, mask, flags, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1486,7 +1486,7 @@ retry:
|
||||
*base = ceph_ino(temp->d_inode);
|
||||
*plen = len;
|
||||
dout("build_path on %p %d built %llx '%.*s'\n",
|
||||
dentry, atomic_read(&dentry->d_count), *base, len, path);
|
||||
dentry, dentry->d_count, *base, len, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
|
||||
extern void ceph_queue_writeback(struct inode *inode);
|
||||
|
||||
extern int ceph_do_getattr(struct inode *inode, int mask);
|
||||
extern int ceph_permission(struct inode *inode, int mask);
|
||||
extern int ceph_permission(struct inode *inode, int mask, unsigned int flags);
|
||||
extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat);
|
||||
|
@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_permission(struct inode *inode, int mask)
|
||||
static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
|
||||
@ -298,7 +301,7 @@ static int cifs_permission(struct inode *inode, int mask)
|
||||
on the client (above and beyond ACL on servers) for
|
||||
servers which do not support setting and viewing mode bits,
|
||||
so allowing client to check permissions is useful */
|
||||
return generic_permission(inode, mask, NULL);
|
||||
return generic_permission(inode, mask, flags, NULL);
|
||||
}
|
||||
|
||||
static struct kmem_cache *cifs_inode_cachep;
|
||||
@ -334,10 +337,17 @@ cifs_alloc_inode(struct super_block *sb)
|
||||
return &cifs_inode->vfs_inode;
|
||||
}
|
||||
|
||||
static void cifs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
|
||||
call_rcu(&inode->i_rcu, cifs_i_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -135,9 +135,9 @@ static void setup_cifs_dentry(struct cifsTconInfo *tcon,
|
||||
struct inode *newinode)
|
||||
{
|
||||
if (tcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
d_instantiate(direntry, newinode);
|
||||
}
|
||||
|
||||
@ -421,9 +421,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
|
||||
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
||||
inode->i_sb, xid);
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
|
||||
if (rc == 0)
|
||||
d_instantiate(direntry, newinode);
|
||||
@ -604,9 +604,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
||||
|
||||
if ((rc == 0) && (newInode != NULL)) {
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
d_add(direntry, newInode);
|
||||
if (posix_open) {
|
||||
filp = lookup_instantiate_filp(nd, direntry,
|
||||
@ -634,9 +634,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
||||
rc = 0;
|
||||
direntry->d_time = jiffies;
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
d_add(direntry, NULL);
|
||||
/* if it was once a directory (but how can we tell?) we could do
|
||||
shrink_dcache_parent(direntry); */
|
||||
@ -656,22 +656,37 @@ lookup_out:
|
||||
static int
|
||||
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
|
||||
{
|
||||
int isValid = 1;
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
if (direntry->d_inode) {
|
||||
if (cifs_revalidate_dentry(direntry))
|
||||
return 0;
|
||||
} else {
|
||||
cFYI(1, "neg dentry 0x%p name = %s",
|
||||
direntry, direntry->d_name.name);
|
||||
if (time_after(jiffies, direntry->d_time + HZ) ||
|
||||
!lookupCacheEnabled) {
|
||||
d_drop(direntry);
|
||||
isValid = 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
/*
|
||||
* This may be nfsd (or something), anyway, we can't see the
|
||||
* intent of this. So, since this can be for creation, drop it.
|
||||
*/
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Drop the negative dentry, in order to make sure to use the
|
||||
* case sensitive name which is specified by user if this is
|
||||
* for creation.
|
||||
*/
|
||||
if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
|
||||
if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* static int cifs_d_delete(struct dentry *direntry)
|
||||
@ -688,9 +703,10 @@ const struct dentry_operations cifs_dentry_ops = {
|
||||
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
|
||||
};
|
||||
|
||||
static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
|
||||
static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *q)
|
||||
{
|
||||
struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
|
||||
struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
|
||||
unsigned long hash;
|
||||
int i;
|
||||
|
||||
@ -703,21 +719,16 @@ static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
|
||||
struct qstr *b)
|
||||
static int cifs_ci_compare(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
|
||||
struct nls_table *codepage = CIFS_SB(pinode->i_sb)->local_nls;
|
||||
|
||||
if ((a->len == b->len) &&
|
||||
(nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
|
||||
/*
|
||||
* To preserve case, don't let an existing negative dentry's
|
||||
* case take precedence. If a is not a negative dentry, this
|
||||
* should have no side effects
|
||||
*/
|
||||
memcpy((void *)a->name, b->name, a->len);
|
||||
if ((name->len == len) &&
|
||||
(nls_strnicmp(codepage, name->name, str, len) == 0))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -809,14 +809,14 @@ inode_has_hashed_dentries(struct inode *inode)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
|
||||
if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1319,9 +1319,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
||||
to set uid/gid */
|
||||
inc_nlink(inode);
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
|
||||
cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
|
||||
cifs_fill_uniqueid(inode->i_sb, &fattr);
|
||||
@ -1363,9 +1363,9 @@ mkdir_get_info:
|
||||
inode->i_sb, xid, NULL);
|
||||
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
d_instantiate(direntry, newinode);
|
||||
/* setting nlink not necessary except in cases where we
|
||||
* failed to get it from the server or was set bogus */
|
||||
|
@ -525,9 +525,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
|
||||
rc);
|
||||
} else {
|
||||
if (pTcon->nocase)
|
||||
direntry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
direntry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(direntry, &cifs_dentry_ops);
|
||||
d_instantiate(direntry, newinode);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
|
||||
cFYI(1, "For %s", name->name);
|
||||
|
||||
if (parent->d_op && parent->d_op->d_hash)
|
||||
parent->d_op->d_hash(parent, name);
|
||||
parent->d_op->d_hash(parent, parent->d_inode, name);
|
||||
else
|
||||
name->hash = full_name_hash(name->name, name->len);
|
||||
|
||||
@ -103,9 +103,9 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
|
||||
}
|
||||
|
||||
if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
|
||||
dentry->d_op = &cifs_ci_dentry_ops;
|
||||
d_set_d_op(dentry, &cifs_ci_dentry_ops);
|
||||
else
|
||||
dentry->d_op = &cifs_dentry_ops;
|
||||
d_set_d_op(dentry, &cifs_dentry_ops);
|
||||
|
||||
alias = d_materialise_unique(dentry, inode);
|
||||
if (alias != NULL) {
|
||||
|
@ -93,7 +93,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
|
||||
struct list_head *child;
|
||||
struct dentry *de;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each(child, &parent->d_subdirs)
|
||||
{
|
||||
de = list_entry(child, struct dentry, d_u.d_child);
|
||||
@ -102,7 +102,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
|
||||
continue;
|
||||
coda_flag_inode(de->d_inode, flag);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&parent->d_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@ -47,7 +48,7 @@ static int coda_readdir(struct file *file, void *buf, filldir_t filldir);
|
||||
|
||||
/* dentry ops */
|
||||
static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd);
|
||||
static int coda_dentry_delete(struct dentry *);
|
||||
static int coda_dentry_delete(const struct dentry *);
|
||||
|
||||
/* support routines */
|
||||
static int coda_venus_readdir(struct file *coda_file, void *buf,
|
||||
@ -125,7 +126,7 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc
|
||||
return ERR_PTR(error);
|
||||
|
||||
exit:
|
||||
entry->d_op = &coda_dentry_operations;
|
||||
d_set_d_op(entry, &coda_dentry_operations);
|
||||
|
||||
if (inode && (type & CODA_NOCACHE))
|
||||
coda_flag_inode(inode, C_VATTR | C_PURGE);
|
||||
@ -134,10 +135,13 @@ exit:
|
||||
}
|
||||
|
||||
|
||||
int coda_permission(struct inode *inode, int mask)
|
||||
int coda_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
||||
|
||||
if (!mask)
|
||||
@ -541,9 +545,13 @@ out:
|
||||
/* called when a cache lookup succeeds */
|
||||
static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
|
||||
{
|
||||
struct inode *inode = de->d_inode;
|
||||
struct inode *inode;
|
||||
struct coda_inode_info *cii;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
inode = de->d_inode;
|
||||
if (!inode || coda_isroot(inode))
|
||||
goto out;
|
||||
if (is_bad_inode(inode))
|
||||
@ -559,7 +567,7 @@ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
|
||||
if (cii->c_flags & C_FLUSH)
|
||||
coda_flag_inode_children(inode, C_FLUSH);
|
||||
|
||||
if (atomic_read(&de->d_count) > 1)
|
||||
if (de->d_count > 1)
|
||||
/* pretend it's valid, but don't change the flags */
|
||||
goto out;
|
||||
|
||||
@ -577,7 +585,7 @@ out:
|
||||
* This is the callback from dput() when d_count is going to 0.
|
||||
* We use this to unhash dentries with bad inodes.
|
||||
*/
|
||||
static int coda_dentry_delete(struct dentry * dentry)
|
||||
static int coda_dentry_delete(const struct dentry * dentry)
|
||||
{
|
||||
int flags;
|
||||
|
||||
|
@ -56,9 +56,16 @@ static struct inode *coda_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void coda_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(coda_inode_cachep, ITOC(inode));
|
||||
}
|
||||
|
||||
static void coda_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(coda_inode_cachep, ITOC(inode));
|
||||
call_rcu(&inode->i_rcu, coda_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <linux/coda_psdev.h>
|
||||
|
||||
/* pioctl ops */
|
||||
static int coda_ioctl_permission(struct inode *inode, int mask);
|
||||
static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags);
|
||||
static long coda_pioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long user_data);
|
||||
|
||||
@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = {
|
||||
};
|
||||
|
||||
/* the coda pioctl inode ops */
|
||||
static int coda_ioctl_permission(struct inode *inode, int mask)
|
||||
static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
return (mask & MAY_EXEC) ? -EACCES : 0;
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
|
||||
{
|
||||
struct config_item * item = NULL;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!d_unhashed(dentry)) {
|
||||
struct configfs_dirent * sd = dentry->d_fsdata;
|
||||
if (sd->s_type & CONFIGFS_ITEM_LINK) {
|
||||
@ -129,7 +129,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
|
||||
} else
|
||||
item = config_item_get(sd->s_element);
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ static void configfs_d_iput(struct dentry * dentry,
|
||||
* We _must_ delete our dentries on last dput, as the chain-to-parent
|
||||
* behavior is required to clear the parents of default_groups.
|
||||
*/
|
||||
static int configfs_d_delete(struct dentry *dentry)
|
||||
static int configfs_d_delete(const struct dentry *dentry)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -232,10 +232,8 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd,
|
||||
|
||||
sd->s_mode = mode;
|
||||
sd->s_dentry = dentry;
|
||||
if (dentry) {
|
||||
if (dentry)
|
||||
dentry->d_fsdata = configfs_get(sd);
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -278,7 +276,6 @@ static int create_dir(struct config_item * k, struct dentry * p,
|
||||
error = configfs_create(d, mode, init_dir);
|
||||
if (!error) {
|
||||
inc_nlink(p->d_inode);
|
||||
(d)->d_op = &configfs_dentry_ops;
|
||||
} else {
|
||||
struct configfs_dirent *sd = d->d_fsdata;
|
||||
if (sd) {
|
||||
@ -371,9 +368,7 @@ int configfs_create_link(struct configfs_symlink *sl,
|
||||
CONFIGFS_ITEM_LINK);
|
||||
if (!err) {
|
||||
err = configfs_create(dentry, mode, init_symlink);
|
||||
if (!err)
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
else {
|
||||
if (err) {
|
||||
struct configfs_dirent *sd = dentry->d_fsdata;
|
||||
if (sd) {
|
||||
spin_lock(&configfs_dirent_lock);
|
||||
@ -399,8 +394,7 @@ static void remove_dir(struct dentry * d)
|
||||
if (d->d_inode)
|
||||
simple_rmdir(parent->d_inode,d);
|
||||
|
||||
pr_debug(" o %s removing done (%d)\n",d->d_name.name,
|
||||
atomic_read(&d->d_count));
|
||||
pr_debug(" o %s removing done (%d)\n",d->d_name.name, d->d_count);
|
||||
|
||||
dput(parent);
|
||||
}
|
||||
@ -448,7 +442,7 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
|
||||
return error;
|
||||
}
|
||||
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
d_set_d_op(dentry, &configfs_dentry_ops);
|
||||
d_rehash(dentry);
|
||||
|
||||
return 0;
|
||||
@ -493,7 +487,11 @@ static struct dentry * configfs_lookup(struct inode *dir,
|
||||
* If it doesn't exist and it isn't a NOT_PINNED item,
|
||||
* it must be negative.
|
||||
*/
|
||||
return simple_lookup(dir, dentry, nd);
|
||||
if (dentry->d_name.len > NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
d_set_d_op(dentry, &configfs_dentry_ops);
|
||||
d_add(dentry, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -685,6 +683,7 @@ static int create_default_group(struct config_group *parent_group,
|
||||
ret = -ENOMEM;
|
||||
child = d_alloc(parent, &name);
|
||||
if (child) {
|
||||
d_set_d_op(child, &configfs_dentry_ops);
|
||||
d_add(child, NULL);
|
||||
|
||||
ret = configfs_attach_group(&parent_group->cg_item,
|
||||
@ -1682,6 +1681,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
|
||||
err = -ENOMEM;
|
||||
dentry = d_alloc(configfs_sb->s_root, &name);
|
||||
if (dentry) {
|
||||
d_set_d_op(dentry, &configfs_dentry_ops);
|
||||
d_add(dentry, NULL);
|
||||
|
||||
err = configfs_attach_group(sd->s_element, &group->cg_item,
|
||||
|
@ -250,18 +250,14 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
|
||||
struct dentry * dentry = sd->s_dentry;
|
||||
|
||||
if (dentry) {
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(d_unhashed(dentry) && dentry->d_inode)) {
|
||||
dget_locked(dentry);
|
||||
dget_dlock(dentry);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
simple_unlink(parent->d_inode, dentry);
|
||||
} else {
|
||||
} else
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
1377
fs/dcache.c
1377
fs/dcache.c
File diff suppressed because it is too large
Load Diff
@ -44,12 +44,17 @@
|
||||
*/
|
||||
static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct dentry *dentry_save;
|
||||
struct vfsmount *vfsmount_save;
|
||||
int rc = 1;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
|
||||
if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
|
||||
goto out;
|
||||
dentry_save = nd->path.dentry;
|
||||
|
@ -260,7 +260,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
|
||||
ecryptfs_dentry->d_parent));
|
||||
lower_inode = lower_dentry->d_inode;
|
||||
fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode);
|
||||
BUG_ON(!atomic_read(&lower_dentry->d_count));
|
||||
BUG_ON(!lower_dentry->d_count);
|
||||
ecryptfs_set_dentry_private(ecryptfs_dentry,
|
||||
kmem_cache_alloc(ecryptfs_dentry_info_cache,
|
||||
GFP_KERNEL));
|
||||
@ -441,7 +441,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
|
||||
struct qstr lower_name;
|
||||
int rc = 0;
|
||||
|
||||
ecryptfs_dentry->d_op = &ecryptfs_dops;
|
||||
d_set_d_op(ecryptfs_dentry, &ecryptfs_dops);
|
||||
if ((ecryptfs_dentry->d_name.len == 1
|
||||
&& !strcmp(ecryptfs_dentry->d_name.name, "."))
|
||||
|| (ecryptfs_dentry->d_name.len == 2
|
||||
@ -454,7 +454,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
|
||||
lower_name.hash = ecryptfs_dentry->d_name.hash;
|
||||
if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
|
||||
rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
|
||||
&lower_name);
|
||||
lower_dir_dentry->d_inode, &lower_name);
|
||||
if (rc < 0)
|
||||
goto out_d_drop;
|
||||
}
|
||||
@ -489,7 +489,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
|
||||
lower_name.hash = full_name_hash(lower_name.name, lower_name.len);
|
||||
if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
|
||||
rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
|
||||
&lower_name);
|
||||
lower_dir_dentry->d_inode, &lower_name);
|
||||
if (rc < 0)
|
||||
goto out_d_drop;
|
||||
}
|
||||
@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
|
||||
}
|
||||
|
||||
static int
|
||||
ecryptfs_permission(struct inode *inode, int mask)
|
||||
ecryptfs_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
return inode_permission(ecryptfs_inode_to_lower(inode), mask);
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
|
||||
if (special_file(lower_inode->i_mode))
|
||||
init_special_inode(inode, lower_inode->i_mode,
|
||||
lower_inode->i_rdev);
|
||||
dentry->d_op = &ecryptfs_dops;
|
||||
d_set_d_op(dentry, &ecryptfs_dops);
|
||||
fsstack_copy_attr_all(inode, lower_inode);
|
||||
/* This size will be overwritten for real files w/ headers and
|
||||
* other metadata */
|
||||
@ -594,7 +594,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
|
||||
deactivate_locked_super(s);
|
||||
goto out;
|
||||
}
|
||||
s->s_root->d_op = &ecryptfs_dops;
|
||||
d_set_d_op(s->s_root, &ecryptfs_dops);
|
||||
s->s_root->d_sb = s;
|
||||
s->s_root->d_parent = s->s_root;
|
||||
|
||||
|
@ -62,6 +62,16 @@ out:
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void ecryptfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
struct ecryptfs_inode_info *inode_info;
|
||||
inode_info = ecryptfs_inode_to_private(inode);
|
||||
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_destroy_inode
|
||||
* @inode: The ecryptfs inode
|
||||
@ -88,7 +98,7 @@ static void ecryptfs_destroy_inode(struct inode *inode)
|
||||
}
|
||||
}
|
||||
ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
|
||||
kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
|
||||
call_rcu(&inode->i_rcu, ecryptfs_i_callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,9 +65,16 @@ static struct inode *efs_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void efs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
|
||||
}
|
||||
|
||||
static void efs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
|
||||
call_rcu(&inode->i_rcu, efs_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -150,12 +150,19 @@ static struct inode *exofs_alloc_inode(struct super_block *sb)
|
||||
return &oi->vfs_inode;
|
||||
}
|
||||
|
||||
static void exofs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(exofs_inode_cachep, exofs_i(inode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an inode from the cache
|
||||
*/
|
||||
static void exofs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(exofs_inode_cachep, exofs_i(inode));
|
||||
call_rcu(&inode->i_rcu, exofs_i_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -43,24 +43,26 @@ find_acceptable_alias(struct dentry *result,
|
||||
void *context)
|
||||
{
|
||||
struct dentry *dentry, *toput = NULL;
|
||||
struct inode *inode;
|
||||
|
||||
if (acceptable(context, result))
|
||||
return result;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
list_for_each_entry(dentry, &result->d_inode->i_dentry, d_alias) {
|
||||
dget_locked(dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
inode = result->d_inode;
|
||||
spin_lock(&inode->i_lock);
|
||||
list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
|
||||
dget(dentry);
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (toput)
|
||||
dput(toput);
|
||||
if (dentry != result && acceptable(context, dentry)) {
|
||||
dput(result);
|
||||
return dentry;
|
||||
}
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
toput = dentry;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
if (toput)
|
||||
dput(toput);
|
||||
|
@ -232,10 +232,17 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
|
||||
}
|
||||
|
||||
int
|
||||
ext2_check_acl(struct inode *inode, int mask)
|
||||
ext2_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU) {
|
||||
if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
|
||||
return -ECHILD;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
|
@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size)
|
||||
#ifdef CONFIG_EXT2_FS_POSIX_ACL
|
||||
|
||||
/* acl.c */
|
||||
extern int ext2_check_acl (struct inode *, int);
|
||||
extern int ext2_check_acl (struct inode *, int, unsigned int);
|
||||
extern int ext2_acl_chmod (struct inode *);
|
||||
extern int ext2_init_acl (struct inode *, struct inode *);
|
||||
|
||||
|
@ -161,9 +161,16 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void ext2_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
|
||||
}
|
||||
|
||||
static void ext2_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
|
||||
call_rcu(&inode->i_rcu, ext2_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -240,10 +240,17 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
|
||||
}
|
||||
|
||||
int
|
||||
ext3_check_acl(struct inode *inode, int mask)
|
||||
ext3_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU) {
|
||||
if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
|
||||
return -ECHILD;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
|
@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size)
|
||||
#ifdef CONFIG_EXT3_FS_POSIX_ACL
|
||||
|
||||
/* acl.c */
|
||||
extern int ext3_check_acl (struct inode *, int);
|
||||
extern int ext3_check_acl (struct inode *, int, unsigned int);
|
||||
extern int ext3_acl_chmod (struct inode *);
|
||||
extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
|
||||
|
||||
|
@ -479,6 +479,13 @@ static struct inode *ext3_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void ext3_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
|
||||
}
|
||||
|
||||
static void ext3_destroy_inode(struct inode *inode)
|
||||
{
|
||||
if (!list_empty(&(EXT3_I(inode)->i_orphan))) {
|
||||
@ -489,7 +496,7 @@ static void ext3_destroy_inode(struct inode *inode)
|
||||
false);
|
||||
dump_stack();
|
||||
}
|
||||
kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
|
||||
call_rcu(&inode->i_rcu, ext3_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -238,10 +238,17 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
||||
}
|
||||
|
||||
int
|
||||
ext4_check_acl(struct inode *inode, int mask)
|
||||
ext4_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU) {
|
||||
if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
|
||||
return -ECHILD;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
|
@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size)
|
||||
#ifdef CONFIG_EXT4_FS_POSIX_ACL
|
||||
|
||||
/* acl.c */
|
||||
extern int ext4_check_acl(struct inode *, int);
|
||||
extern int ext4_check_acl(struct inode *, int, unsigned int);
|
||||
extern int ext4_acl_chmod(struct inode *);
|
||||
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
|
||||
|
||||
|
@ -841,6 +841,13 @@ static int ext4_drop_inode(struct inode *inode)
|
||||
return drop;
|
||||
}
|
||||
|
||||
static void ext4_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
|
||||
}
|
||||
|
||||
static void ext4_destroy_inode(struct inode *inode)
|
||||
{
|
||||
ext4_ioend_wait(inode);
|
||||
@ -853,7 +860,7 @@ static void ext4_destroy_inode(struct inode *inode)
|
||||
true);
|
||||
dump_stack();
|
||||
}
|
||||
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
|
||||
call_rcu(&inode->i_rcu, ext4_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
@ -514,9 +514,16 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void fat_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
|
||||
}
|
||||
|
||||
static void fat_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
|
||||
call_rcu(&inode->i_rcu, fat_i_callback);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
@ -743,7 +750,7 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb,
|
||||
*/
|
||||
result = d_obtain_alias(inode);
|
||||
if (!IS_ERR(result))
|
||||
result->d_op = sb->s_root->d_op;
|
||||
d_set_d_op(result, sb->s_root->d_op);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -793,7 +800,7 @@ static struct dentry *fat_get_parent(struct dentry *child)
|
||||
|
||||
parent = d_obtain_alias(inode);
|
||||
if (!IS_ERR(parent))
|
||||
parent->d_op = sb->s_root->d_op;
|
||||
d_set_d_op(parent, sb->s_root->d_op);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
|
||||
|
@ -148,7 +148,8 @@ static int msdos_find(struct inode *dir, const unsigned char *name, int len,
|
||||
* that the existing dentry can be used. The msdos fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
|
||||
static int msdos_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
@ -164,16 +165,18 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
|
||||
* Compare two msdos names. If either of the names are invalid,
|
||||
* we fall back to doing the standard name comparison.
|
||||
*/
|
||||
static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
|
||||
struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
|
||||
unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
|
||||
int error;
|
||||
|
||||
error = msdos_format_name(a->name, a->len, a_msdos_name, options);
|
||||
error = msdos_format_name(name->name, name->len, a_msdos_name, options);
|
||||
if (error)
|
||||
goto old_compare;
|
||||
error = msdos_format_name(b->name, b->len, b_msdos_name, options);
|
||||
error = msdos_format_name(str, len, b_msdos_name, options);
|
||||
if (error)
|
||||
goto old_compare;
|
||||
error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
|
||||
@ -182,8 +185,8 @@ out:
|
||||
|
||||
old_compare:
|
||||
error = 1;
|
||||
if (a->len == b->len)
|
||||
error = memcmp(a->name, b->name, a->len);
|
||||
if (name->len == len)
|
||||
error = memcmp(name->name, str, len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -224,10 +227,10 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
out:
|
||||
unlock_super(sb);
|
||||
dentry->d_op = &msdos_dentry_operations;
|
||||
d_set_d_op(dentry, &msdos_dentry_operations);
|
||||
dentry = d_splice_alias(inode, dentry);
|
||||
if (dentry)
|
||||
dentry->d_op = &msdos_dentry_operations;
|
||||
d_set_d_op(dentry, &msdos_dentry_operations);
|
||||
return dentry;
|
||||
|
||||
error:
|
||||
@ -670,7 +673,7 @@ static int msdos_fill_super(struct super_block *sb, void *data, int silent)
|
||||
}
|
||||
|
||||
sb->s_flags |= MS_NOATIME;
|
||||
sb->s_root->d_op = &msdos_dentry_operations;
|
||||
d_set_d_op(sb->s_root, &msdos_dentry_operations);
|
||||
unlock_super(sb);
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,6 +43,9 @@ static int vfat_revalidate_shortname(struct dentry *dentry)
|
||||
|
||||
static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
/* This is not negative dentry. Always valid. */
|
||||
if (dentry->d_inode)
|
||||
return 1;
|
||||
@ -51,6 +54,9 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
|
||||
static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
/*
|
||||
* This is not negative dentry. Always valid.
|
||||
*
|
||||
@ -85,22 +91,26 @@ static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
|
||||
}
|
||||
|
||||
/* returns the length of a struct qstr, ignoring trailing dots */
|
||||
static unsigned int vfat_striptail_len(struct qstr *qstr)
|
||||
static unsigned int __vfat_striptail_len(unsigned int len, const char *name)
|
||||
{
|
||||
unsigned int len = qstr->len;
|
||||
|
||||
while (len && qstr->name[len - 1] == '.')
|
||||
while (len && name[len - 1] == '.')
|
||||
len--;
|
||||
return len;
|
||||
}
|
||||
|
||||
static unsigned int vfat_striptail_len(const struct qstr *qstr)
|
||||
{
|
||||
return __vfat_striptail_len(qstr->len, qstr->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the hash for the vfat name corresponding to the dentry.
|
||||
* Note: if the name is invalid, we leave the hash code unchanged so
|
||||
* that the existing dentry can be used. The vfat fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
|
||||
static int vfat_hash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
|
||||
return 0;
|
||||
@ -112,9 +122,10 @@ static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
|
||||
* that the existing dentry can be used. The vfat fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
|
||||
static int vfat_hashi(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *qstr)
|
||||
{
|
||||
struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
|
||||
struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
|
||||
const unsigned char *name;
|
||||
unsigned int len;
|
||||
unsigned long hash;
|
||||
@ -133,16 +144,18 @@ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
|
||||
/*
|
||||
* Case insensitive compare of two vfat names.
|
||||
*/
|
||||
static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
|
||||
struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
|
||||
unsigned int alen, blen;
|
||||
|
||||
/* A filename cannot end in '.' or we treat it like it has none */
|
||||
alen = vfat_striptail_len(a);
|
||||
blen = vfat_striptail_len(b);
|
||||
alen = vfat_striptail_len(name);
|
||||
blen = __vfat_striptail_len(len, str);
|
||||
if (alen == blen) {
|
||||
if (nls_strnicmp(t, a->name, b->name, alen) == 0)
|
||||
if (nls_strnicmp(t, name->name, str, alen) == 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@ -151,15 +164,17 @@ static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
/*
|
||||
* Case sensitive compare of two vfat names.
|
||||
*/
|
||||
static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
static int vfat_cmp(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
unsigned int alen, blen;
|
||||
|
||||
/* A filename cannot end in '.' or we treat it like it has none */
|
||||
alen = vfat_striptail_len(a);
|
||||
blen = vfat_striptail_len(b);
|
||||
alen = vfat_striptail_len(name);
|
||||
blen = __vfat_striptail_len(len, str);
|
||||
if (alen == blen) {
|
||||
if (strncmp(a->name, b->name, alen) == 0)
|
||||
if (strncmp(name->name, str, alen) == 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@ -757,11 +772,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
out:
|
||||
unlock_super(sb);
|
||||
dentry->d_op = sb->s_root->d_op;
|
||||
d_set_d_op(dentry, sb->s_root->d_op);
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
dentry = d_splice_alias(inode, dentry);
|
||||
if (dentry) {
|
||||
dentry->d_op = sb->s_root->d_op;
|
||||
d_set_d_op(dentry, sb->s_root->d_op);
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
}
|
||||
return dentry;
|
||||
@ -1063,9 +1078,9 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent)
|
||||
}
|
||||
|
||||
if (MSDOS_SB(sb)->options.name_check != 's')
|
||||
sb->s_root->d_op = &vfat_ci_dentry_ops;
|
||||
d_set_d_op(sb->s_root, &vfat_ci_dentry_ops);
|
||||
else
|
||||
sb->s_root->d_op = &vfat_dentry_ops;
|
||||
d_set_d_op(sb->s_root, &vfat_dentry_ops);
|
||||
|
||||
unlock_super(sb);
|
||||
return 0;
|
||||
|
@ -115,6 +115,9 @@ int unregister_filesystem(struct file_system_type * fs)
|
||||
tmp = &(*tmp)->next;
|
||||
}
|
||||
write_unlock(&file_systems_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -337,6 +337,13 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
|
||||
return ip;
|
||||
}
|
||||
|
||||
static void vxfs_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(vxfs_inode_cachep, inode->i_private);
|
||||
}
|
||||
|
||||
/**
|
||||
* vxfs_evict_inode - remove inode from main memory
|
||||
* @ip: inode to discard.
|
||||
@ -350,5 +357,5 @@ vxfs_evict_inode(struct inode *ip)
|
||||
{
|
||||
truncate_inode_pages(&ip->i_data, 0);
|
||||
end_writeback(ip);
|
||||
kmem_cache_free(vxfs_inode_cachep, ip->i_private);
|
||||
call_rcu(&ip->i_rcu, vxfs_i_callback);
|
||||
}
|
||||
|
@ -14,12 +14,14 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
|
||||
struct path old_root;
|
||||
|
||||
spin_lock(&fs->lock);
|
||||
write_seqcount_begin(&fs->seq);
|
||||
old_root = fs->root;
|
||||
fs->root = *path;
|
||||
path_get(path);
|
||||
path_get_long(path);
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
if (old_root.dentry)
|
||||
path_put(&old_root);
|
||||
path_put_long(&old_root);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -31,13 +33,15 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path)
|
||||
struct path old_pwd;
|
||||
|
||||
spin_lock(&fs->lock);
|
||||
write_seqcount_begin(&fs->seq);
|
||||
old_pwd = fs->pwd;
|
||||
fs->pwd = *path;
|
||||
path_get(path);
|
||||
path_get_long(path);
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
|
||||
if (old_pwd.dentry)
|
||||
path_put(&old_pwd);
|
||||
path_put_long(&old_pwd);
|
||||
}
|
||||
|
||||
void chroot_fs_refs(struct path *old_root, struct path *new_root)
|
||||
@ -52,31 +56,33 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
|
||||
fs = p->fs;
|
||||
if (fs) {
|
||||
spin_lock(&fs->lock);
|
||||
write_seqcount_begin(&fs->seq);
|
||||
if (fs->root.dentry == old_root->dentry
|
||||
&& fs->root.mnt == old_root->mnt) {
|
||||
path_get(new_root);
|
||||
path_get_long(new_root);
|
||||
fs->root = *new_root;
|
||||
count++;
|
||||
}
|
||||
if (fs->pwd.dentry == old_root->dentry
|
||||
&& fs->pwd.mnt == old_root->mnt) {
|
||||
path_get(new_root);
|
||||
path_get_long(new_root);
|
||||
fs->pwd = *new_root;
|
||||
count++;
|
||||
}
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
}
|
||||
task_unlock(p);
|
||||
} while_each_thread(g, p);
|
||||
read_unlock(&tasklist_lock);
|
||||
while (count--)
|
||||
path_put(old_root);
|
||||
path_put_long(old_root);
|
||||
}
|
||||
|
||||
void free_fs_struct(struct fs_struct *fs)
|
||||
{
|
||||
path_put(&fs->root);
|
||||
path_put(&fs->pwd);
|
||||
path_put_long(&fs->root);
|
||||
path_put_long(&fs->pwd);
|
||||
kmem_cache_free(fs_cachep, fs);
|
||||
}
|
||||
|
||||
@ -88,8 +94,10 @@ void exit_fs(struct task_struct *tsk)
|
||||
int kill;
|
||||
task_lock(tsk);
|
||||
spin_lock(&fs->lock);
|
||||
write_seqcount_begin(&fs->seq);
|
||||
tsk->fs = NULL;
|
||||
kill = !--fs->users;
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
task_unlock(tsk);
|
||||
if (kill)
|
||||
@ -105,8 +113,15 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
|
||||
fs->users = 1;
|
||||
fs->in_exec = 0;
|
||||
spin_lock_init(&fs->lock);
|
||||
seqcount_init(&fs->seq);
|
||||
fs->umask = old->umask;
|
||||
get_fs_root_and_pwd(old, &fs->root, &fs->pwd);
|
||||
|
||||
spin_lock(&old->lock);
|
||||
fs->root = old->root;
|
||||
path_get_long(&fs->root);
|
||||
fs->pwd = old->pwd;
|
||||
path_get_long(&fs->pwd);
|
||||
spin_unlock(&old->lock);
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
@ -144,6 +159,7 @@ EXPORT_SYMBOL(current_umask);
|
||||
struct fs_struct init_fs = {
|
||||
.users = 1,
|
||||
.lock = __SPIN_LOCK_UNLOCKED(init_fs.lock),
|
||||
.seq = SEQCNT_ZERO,
|
||||
.umask = 0022,
|
||||
};
|
||||
|
||||
|
@ -156,8 +156,12 @@ u64 fuse_get_attr_version(struct fuse_conn *fc)
|
||||
*/
|
||||
static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
|
||||
{
|
||||
struct inode *inode = entry->d_inode;
|
||||
struct inode *inode;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
inode = entry->d_inode;
|
||||
if (inode && is_bad_inode(inode))
|
||||
return 0;
|
||||
else if (fuse_dentry_time(entry) < get_jiffies_64()) {
|
||||
@ -347,7 +351,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
||||
}
|
||||
|
||||
entry = newent ? newent : entry;
|
||||
entry->d_op = &fuse_dentry_operations;
|
||||
d_set_d_op(entry, &fuse_dentry_operations);
|
||||
if (outarg_valid)
|
||||
fuse_change_entry_timeout(entry, &outarg);
|
||||
else
|
||||
@ -981,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask)
|
||||
* access request is sent. Execute permission is still checked
|
||||
* locally based on file mode.
|
||||
*/
|
||||
static int fuse_permission(struct inode *inode, int mask)
|
||||
static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
bool refreshed = false;
|
||||
int err = 0;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
if (!fuse_allow_task(fc, current))
|
||||
return -EACCES;
|
||||
|
||||
@ -1001,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask)
|
||||
}
|
||||
|
||||
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
|
||||
err = generic_permission(inode, mask, NULL);
|
||||
err = generic_permission(inode, mask, flags, NULL);
|
||||
|
||||
/* If permission is denied, try to refresh file
|
||||
attributes. This is also needed, because the root
|
||||
@ -1009,7 +1016,8 @@ static int fuse_permission(struct inode *inode, int mask)
|
||||
if (err == -EACCES && !refreshed) {
|
||||
err = fuse_do_getattr(inode, NULL, NULL);
|
||||
if (!err)
|
||||
err = generic_permission(inode, mask, NULL);
|
||||
err = generic_permission(inode, mask,
|
||||
flags, NULL);
|
||||
}
|
||||
|
||||
/* Note: the opposite of the above test does not
|
||||
|
@ -99,6 +99,13 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void fuse_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(fuse_inode_cachep, inode);
|
||||
}
|
||||
|
||||
static void fuse_destroy_inode(struct inode *inode)
|
||||
{
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
@ -106,7 +113,7 @@ static void fuse_destroy_inode(struct inode *inode)
|
||||
BUG_ON(!list_empty(&fi->queued_writes));
|
||||
if (fi->forget_req)
|
||||
fuse_request_free(fi->forget_req);
|
||||
kmem_cache_free(fuse_inode_cachep, inode);
|
||||
call_rcu(&inode->i_rcu, fuse_i_callback);
|
||||
}
|
||||
|
||||
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
||||
@ -619,7 +626,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
|
||||
|
||||
entry = d_obtain_alias(inode);
|
||||
if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID) {
|
||||
entry->d_op = &fuse_dentry_operations;
|
||||
d_set_d_op(entry, &fuse_dentry_operations);
|
||||
fuse_invalidate_entry_cache(entry);
|
||||
}
|
||||
|
||||
@ -721,7 +728,7 @@ static struct dentry *fuse_get_parent(struct dentry *child)
|
||||
|
||||
parent = d_obtain_alias(inode);
|
||||
if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID) {
|
||||
parent->d_op = &fuse_dentry_operations;
|
||||
d_set_d_op(parent, &fuse_dentry_operations);
|
||||
fuse_invalidate_entry_cache(parent);
|
||||
}
|
||||
|
||||
|
@ -190,14 +190,20 @@ generic_acl_chmod(struct inode *inode)
|
||||
}
|
||||
|
||||
int
|
||||
generic_check_acl(struct inode *inode, int mask)
|
||||
generic_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (flags & IPERM_FLAG_RCU) {
|
||||
if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
|
||||
return -ECHILD;
|
||||
} else {
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (acl) {
|
||||
int error = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (acl) {
|
||||
int error = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
|
||||
* Returns: errno
|
||||
*/
|
||||
|
||||
int gfs2_check_acl(struct inode *inode, int mask)
|
||||
int gfs2_check_acl(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default"
|
||||
#define GFS2_ACL_MAX_ENTRIES 25
|
||||
|
||||
extern int gfs2_check_acl(struct inode *inode, int mask);
|
||||
extern int gfs2_check_acl(struct inode *inode, int mask, unsigned int);
|
||||
extern int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode);
|
||||
extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
|
||||
extern const struct xattr_handler gfs2_xattr_system_handler;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/gfs2_ondisk.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
@ -34,15 +35,23 @@
|
||||
|
||||
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode);
|
||||
struct gfs2_inode *dip = GFS2_I(parent->d_inode);
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct dentry *parent;
|
||||
struct gfs2_sbd *sdp;
|
||||
struct gfs2_inode *dip;
|
||||
struct inode *inode;
|
||||
struct gfs2_holder d_gh;
|
||||
struct gfs2_inode *ip = NULL;
|
||||
int error;
|
||||
int had_lock = 0;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
sdp = GFS2_SB(parent->d_inode);
|
||||
dip = GFS2_I(parent->d_inode);
|
||||
inode = dentry->d_inode;
|
||||
|
||||
if (inode) {
|
||||
if (is_bad_inode(inode))
|
||||
goto invalid;
|
||||
@ -100,13 +109,14 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gfs2_dhash(struct dentry *dentry, struct qstr *str)
|
||||
static int gfs2_dhash(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *str)
|
||||
{
|
||||
str->hash = gfs2_disk_hash(str->name, str->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gfs2_dentry_delete(struct dentry *dentry)
|
||||
static int gfs2_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
struct gfs2_inode *ginode;
|
||||
|
||||
|
@ -130,7 +130,7 @@ static struct dentry *gfs2_get_parent(struct dentry *child)
|
||||
|
||||
dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &gfs2_qdotdot, 1));
|
||||
if (!IS_ERR(dentry))
|
||||
dentry->d_op = &gfs2_dops;
|
||||
d_set_d_op(dentry, &gfs2_dops);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
|
||||
out_inode:
|
||||
dentry = d_obtain_alias(inode);
|
||||
if (!IS_ERR(dentry))
|
||||
dentry->d_op = &gfs2_dops;
|
||||
d_set_d_op(dentry, &gfs2_dops);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
|
||||
!capable(CAP_LINUX_IMMUTABLE))
|
||||
goto out;
|
||||
if (!IS_IMMUTABLE(inode)) {
|
||||
error = gfs2_permission(inode, MAY_WRITE);
|
||||
error = gfs2_permission(inode, MAY_WRITE, 0);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
|
||||
}
|
||||
|
||||
if (!is_root) {
|
||||
error = gfs2_permission(dir, MAY_EXEC);
|
||||
error = gfs2_permission(dir, MAY_EXEC, 0);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
|
||||
{
|
||||
int error;
|
||||
|
||||
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
|
||||
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -113,7 +113,7 @@ extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
|
||||
extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
|
||||
const struct qstr *name,
|
||||
unsigned int mode, dev_t dev);
|
||||
extern int gfs2_permission(struct inode *inode, int mask);
|
||||
extern int gfs2_permission(struct inode *inode, int mask, unsigned int flags);
|
||||
extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
|
||||
extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
|
||||
extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
|
||||
|
@ -440,7 +440,7 @@ static int gfs2_lookup_root(struct super_block *sb, struct dentry **dptr,
|
||||
iput(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dentry->d_op = &gfs2_dops;
|
||||
d_set_d_op(dentry, &gfs2_dops);
|
||||
*dptr = dentry;
|
||||
return 0;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
|
||||
dentry->d_op = &gfs2_dops;
|
||||
d_set_d_op(dentry, &gfs2_dops);
|
||||
|
||||
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
|
||||
if (inode && IS_ERR(inode))
|
||||
@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
|
||||
if (error)
|
||||
goto out_child;
|
||||
|
||||
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
|
||||
@ -289,7 +289,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
|
||||
if (IS_APPEND(&dip->i_inode))
|
||||
return -EPERM;
|
||||
|
||||
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
|
||||
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC);
|
||||
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
|
||||
@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
||||
/* Check out the dir to be renamed */
|
||||
|
||||
if (dir_rename) {
|
||||
error = gfs2_permission(odentry->d_inode, MAY_WRITE);
|
||||
error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
}
|
||||
@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
|
||||
* Returns: errno
|
||||
*/
|
||||
|
||||
int gfs2_permission(struct inode *inode, int mask)
|
||||
int gfs2_permission(struct inode *inode, int mask, unsigned int flags)
|
||||
{
|
||||
struct gfs2_inode *ip = GFS2_I(inode);
|
||||
struct gfs2_inode *ip;
|
||||
struct gfs2_holder i_gh;
|
||||
int error;
|
||||
int unlock = 0;
|
||||
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
ip = GFS2_I(inode);
|
||||
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
|
||||
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
|
||||
if (error)
|
||||
@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask)
|
||||
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
|
||||
error = -EACCES;
|
||||
else
|
||||
error = generic_permission(inode, mask, gfs2_check_acl);
|
||||
error = generic_permission(inode, mask, flags, gfs2_check_acl);
|
||||
if (unlock)
|
||||
gfs2_glock_dq_uninit(&i_gh);
|
||||
|
||||
|
@ -1405,9 +1405,16 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb)
|
||||
return &ip->i_inode;
|
||||
}
|
||||
|
||||
static void gfs2_i_callback(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
INIT_LIST_HEAD(&inode->i_dentry);
|
||||
kmem_cache_free(gfs2_inode_cachep, inode);
|
||||
}
|
||||
|
||||
static void gfs2_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(gfs2_inode_cachep, inode);
|
||||
call_rcu(&inode->i_rcu, gfs2_i_callback);
|
||||
}
|
||||
|
||||
const struct super_operations gfs2_super_ops = {
|
||||
|
@ -25,7 +25,7 @@ static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct inode *inode = NULL;
|
||||
int res;
|
||||
|
||||
dentry->d_op = &hfs_dentry_operations;
|
||||
d_set_d_op(dentry, &hfs_dentry_operations);
|
||||
|
||||
hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
|
||||
hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
|
||||
|
@ -213,10 +213,14 @@ extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
|
||||
/* string.c */
|
||||
extern const struct dentry_operations hfs_dentry_operations;
|
||||
|
||||
extern int hfs_hash_dentry(struct dentry *, struct qstr *);
|
||||
extern int hfs_hash_dentry(const struct dentry *, const struct inode *,
|
||||
struct qstr *);
|
||||
extern int hfs_strcmp(const unsigned char *, unsigned int,
|
||||
const unsigned char *, unsigned int);
|
||||
extern int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
extern int hfs_compare_dentry(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name);
|
||||
|
||||
/* trans.c */
|
||||
extern void hfs_asc2mac(struct super_block *, struct hfs_name *, struct qstr *);
|
||||
|
@ -51,7 +51,8 @@ static unsigned char caseorder[256] = {
|
||||
/*
|
||||
* Hash a string to an integer in a case-independent way
|
||||
*/
|
||||
int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
|
||||
int hfs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
|
||||
struct qstr *this)
|
||||
{
|
||||
const unsigned char *name = this->name;
|
||||
unsigned int hash, len = this->len;
|
||||
@ -92,21 +93,21 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1,
|
||||
* Test for equality of two strings in the HFS filename character ordering.
|
||||
* return 1 on failure and 0 on success
|
||||
*/
|
||||
int hfs_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2)
|
||||
int hfs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
const unsigned char *n1, *n2;
|
||||
int len;
|
||||
|
||||
len = s1->len;
|
||||
if (len >= HFS_NAMELEN) {
|
||||
if (s2->len < HFS_NAMELEN)
|
||||
if (name->len < HFS_NAMELEN)
|
||||
return 1;
|
||||
len = HFS_NAMELEN;
|
||||
} else if (len != s2->len)
|
||||
} else if (len != name->len)
|
||||
return 1;
|
||||
|
||||
n1 = s1->name;
|
||||
n2 = s2->name;
|
||||
n1 = str;
|
||||
n2 = name->name;
|
||||
while (len--) {
|
||||
if (caseorder[*n1++] != caseorder[*n2++])
|
||||
return 1;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user