diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 13:23:02 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 13:23:03 -0800 |
commit | 56a79b7b021bf1b08334e63c2c14b280e2dbf47a (patch) | |
tree | 0419233e6194f4f12073c9284852885aa8984bec /fs | |
parent | 1c82315a12144cde732636e259d39e3ee81b3c5b (diff) | |
parent | dcf787f39162ce32ca325b3e784aba2d2444619a (diff) | |
download | op-kernel-dev-56a79b7b021bf1b08334e63c2c14b280e2dbf47a.zip op-kernel-dev-56a79b7b021bf1b08334e63c2c14b280e2dbf47a.tar.gz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull more VFS bits from Al Viro:
"Unfortunately, it looks like xattr series will have to wait until the
next cycle ;-/
This pile contains 9p cleanups and fixes (races in v9fs_fid_add()
etc), fixup for nommu breakage in shmem.c, several cleanups and a bit
more file_inode() work"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
constify path_get/path_put and fs_struct.c stuff
fix nommu breakage in shmem.c
cache the value of file_inode() in struct file
9p: if v9fs_fid_lookup() gets to asking server, it'd better have hashed dentry
9p: make sure ->lookup() adds fid to the right dentry
9p: untangle ->lookup() a bit
9p: double iput() in ->lookup() if d_materialise_unique() fails
9p: v9fs_fid_add() can't fail now
v9fs: get rid of v9fs_dentry
9p: turn fid->dlist into hlist
9p: don't bother with private lock in ->d_fsdata; dentry->d_lock will do just fine
more file_inode() open-coded instances
selinux: opened file can't have NULL or negative ->f_path.dentry
(In the meantime, the hlist traversal macros have changed, so this
required a semantic conflict fixup for the newly hlistified fid->dlist)
Diffstat (limited to 'fs')
-rw-r--r-- | fs/9p/fid.c | 54 | ||||
-rw-r--r-- | fs/9p/fid.h | 22 | ||||
-rw-r--r-- | fs/9p/vfs_dentry.c | 17 | ||||
-rw-r--r-- | fs/9p/vfs_inode.c | 40 | ||||
-rw-r--r-- | fs/9p/vfs_inode_dotl.c | 19 | ||||
-rw-r--r-- | fs/ext4/indirect.c | 2 | ||||
-rw-r--r-- | fs/f2fs/file.c | 6 | ||||
-rw-r--r-- | fs/file_table.c | 2 | ||||
-rw-r--r-- | fs/fs_struct.c | 6 | ||||
-rw-r--r-- | fs/fuse/dev.c | 2 | ||||
-rw-r--r-- | fs/fuse/file.c | 24 | ||||
-rw-r--r-- | fs/internal.h | 2 | ||||
-rw-r--r-- | fs/namei.c | 4 | ||||
-rw-r--r-- | fs/open.c | 3 | ||||
-rw-r--r-- | fs/seq_file.c | 2 |
15 files changed, 79 insertions, 126 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c index afd4724..d51ec9f 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -41,29 +41,16 @@ * */ -int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) +static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid) { - struct v9fs_dentry *dent; - - p9_debug(P9_DEBUG_VFS, "fid %d dentry %s\n", - fid->fid, dentry->d_name.name); - - dent = dentry->d_fsdata; - if (!dent) { - dent = kmalloc(sizeof(struct v9fs_dentry), GFP_KERNEL); - if (!dent) - return -ENOMEM; - - spin_lock_init(&dent->lock); - INIT_LIST_HEAD(&dent->fidlist); - dentry->d_fsdata = dent; - } - - spin_lock(&dent->lock); - list_add(&fid->dlist, &dent->fidlist); - spin_unlock(&dent->lock); + hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata); +} - return 0; +void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) +{ + spin_lock(&dentry->d_lock); + __add_fid(dentry, fid); + spin_unlock(&dentry->d_lock); } /** @@ -76,23 +63,23 @@ int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any) { - struct v9fs_dentry *dent; struct p9_fid *fid, *ret; p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n", dentry->d_name.name, dentry, from_kuid(&init_user_ns, uid), any); - dent = (struct v9fs_dentry *) dentry->d_fsdata; ret = NULL; - if (dent) { - spin_lock(&dent->lock); - list_for_each_entry(fid, &dent->fidlist, dlist) { + /* we'll recheck under lock if there's anything to look in */ + if (dentry->d_fsdata) { + struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata; + spin_lock(&dentry->d_lock); + hlist_for_each_entry(fid, h, dlist) { if (any || uid_eq(fid->uid, uid)) { ret = fid; break; } } - spin_unlock(&dent->lock); + spin_unlock(&dentry->d_lock); } return ret; @@ -215,8 +202,17 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, } kfree(wnames); fid_out: - if (!IS_ERR(fid)) - v9fs_fid_add(dentry, fid); + if (!IS_ERR(fid)) { + spin_lock(&dentry->d_lock); + if (d_unhashed(dentry)) { + spin_unlock(&dentry->d_lock); + p9_client_clunk(fid); + fid = ERR_PTR(-ENOENT); + } else { + __add_fid(dentry, fid); + spin_unlock(&dentry->d_lock); + } + } err_out: up_read(&v9ses->rename_sem); return fid; diff --git a/fs/9p/fid.h b/fs/9p/fid.h index bb0b6e7..2b6787f 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h @@ -23,28 +23,8 @@ #define FS_9P_FID_H #include <linux/list.h> -/** - * struct v9fs_dentry - 9p private data stored in dentry d_fsdata - * @lock: protects the fidlist - * @fidlist: list of FIDs currently associated with this dentry - * - * This structure defines the 9p private data associated with - * a particular dentry. In particular, this private data is used - * to lookup which 9P FID handle should be used for a particular VFS - * operation. FID handles are associated with dentries instead of - * inodes in order to more closely map functionality to the Plan 9 - * expected behavior for FID reclaimation and tracking. - * - * See Also: Mapping FIDs to Linux VFS model in - * Design and Implementation of the Linux 9P File System documentation - */ -struct v9fs_dentry { - spinlock_t lock; /* protect fidlist */ - struct list_head fidlist; -}; - struct p9_fid *v9fs_fid_lookup(struct dentry *dentry); struct p9_fid *v9fs_fid_clone(struct dentry *dentry); -int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); +void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); struct p9_fid *v9fs_writeback_fid(struct dentry *dentry); #endif diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index 9ad6862..f039b10 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -83,21 +83,12 @@ static int v9fs_cached_dentry_delete(const struct dentry *dentry) static void v9fs_dentry_release(struct dentry *dentry) { - struct v9fs_dentry *dent; - struct p9_fid *temp, *current_fid; - + struct hlist_node *p, *n; p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name, dentry); - dent = dentry->d_fsdata; - if (dent) { - list_for_each_entry_safe(current_fid, temp, &dent->fidlist, - dlist) { - p9_client_clunk(current_fid); - } - - kfree(dent); - dentry->d_fsdata = NULL; - } + hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata) + p9_client_clunk(hlist_entry(p, struct p9_fid, dlist)); + dentry->d_fsdata = NULL; } static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index b5340c8..d86edc8 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -692,9 +692,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, "inode creation failed %d\n", err); goto error; } - err = v9fs_fid_add(dentry, fid); - if (err < 0) - goto error; + v9fs_fid_add(dentry, fid); d_instantiate(dentry, inode); } return ofid; @@ -790,7 +788,6 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, struct p9_fid *dfid, *fid; struct inode *inode; char *name; - int result = 0; p9_debug(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p flags: %x\n", dir, dentry->d_name.name, dentry, flags); @@ -808,13 +805,11 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, name = (char *) dentry->d_name.name; fid = p9_client_walk(dfid, 1, &name, 1); if (IS_ERR(fid)) { - result = PTR_ERR(fid); - if (result == -ENOENT) { - inode = NULL; - goto inst_out; + if (fid == ERR_PTR(-ENOENT)) { + d_add(dentry, NULL); + return NULL; } - - return ERR_PTR(result); + return ERR_CAST(fid); } /* * Make sure we don't use a wrong inode due to parallel @@ -826,14 +821,9 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, else inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); if (IS_ERR(inode)) { - result = PTR_ERR(inode); - inode = NULL; - goto error; + p9_client_clunk(fid); + return ERR_CAST(inode); } - result = v9fs_fid_add(dentry, fid); - if (result < 0) - goto error_iput; -inst_out: /* * If we had a rename on the server and a parallel lookup * for the new name, then make sure we instantiate with @@ -842,15 +832,13 @@ inst_out: * k/b. */ res = d_materialise_unique(dentry, inode); - if (!IS_ERR(res)) - return res; - result = PTR_ERR(res); -error_iput: - iput(inode); -error: - p9_client_clunk(fid); - - return ERR_PTR(result); + if (!res) + v9fs_fid_add(dentry, fid); + else if (!IS_ERR(res)) + v9fs_fid_add(res, fid); + else + p9_client_clunk(fid); + return res; } static int diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 61e4fa7..53687bb 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -333,9 +333,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, /* Now set the ACL based on the default value */ v9fs_set_create_acl(inode, fid, dacl, pacl); - err = v9fs_fid_add(dentry, fid); - if (err < 0) - goto error; + v9fs_fid_add(dentry, fid); d_instantiate(dentry, inode); v9inode = V9FS_I(inode); @@ -453,12 +451,11 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, err); goto error; } - err = v9fs_fid_add(dentry, fid); - if (err < 0) - goto error; + v9fs_fid_add(dentry, fid); v9fs_set_create_acl(inode, fid, dacl, pacl); d_instantiate(dentry, inode); fid = NULL; + err = 0; } else { /* * Not in cached mode. No need to populate @@ -747,11 +744,10 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry, err); goto error; } - err = v9fs_fid_add(dentry, fid); - if (err < 0) - goto error; + v9fs_fid_add(dentry, fid); d_instantiate(dentry, inode); fid = NULL; + err = 0; } else { /* Not in cached mode. No need to populate inode with stat */ inode = v9fs_get_inode(dir->i_sb, S_IFLNK, 0); @@ -900,11 +896,10 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode, goto error; } v9fs_set_create_acl(inode, fid, dacl, pacl); - err = v9fs_fid_add(dentry, fid); - if (err < 0) - goto error; + v9fs_fid_add(dentry, fid); d_instantiate(dentry, inode); fid = NULL; + err = 0; } else { /* * Not in cached mode. No need to populate inode with stat. diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index c541ab8..b505a14 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -1606,7 +1606,7 @@ err: int ext4_ind_punch_hole(struct file *file, loff_t offset, loff_t length) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; ext4_lblk_t first_block, stop_block; struct address_space *mapping = inode->i_mapping; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b7a053d..958a46d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -29,7 +29,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page = vmf->page; - struct inode *inode = vma->vm_file->f_path.dentry->d_inode; + struct inode *inode = file_inode(vma->vm_file); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); block_t old_blk_addr; struct dnode_of_data dn; @@ -544,7 +544,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset, static long f2fs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); long ret; if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) @@ -577,7 +577,7 @@ static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = file_inode(filp); struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int flags; int ret; diff --git a/fs/file_table.c b/fs/file_table.c index aa07d36..cd4d87a 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -176,6 +176,7 @@ struct file *alloc_file(struct path *path, fmode_t mode, return file; file->f_path = *path; + file->f_inode = path->dentry->d_inode; file->f_mapping = path->dentry->d_inode->i_mapping; file->f_mode = mode; file->f_op = fop; @@ -258,6 +259,7 @@ static void __fput(struct file *file) drop_file_write_access(file); file->f_path.dentry = NULL; file->f_path.mnt = NULL; + file->f_inode = NULL; file_free(file); dput(dentry); mntput(mnt); diff --git a/fs/fs_struct.c b/fs/fs_struct.c index fe6ca58..d8ac61d 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -10,7 +10,7 @@ * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. * It can block. */ -void set_fs_root(struct fs_struct *fs, struct path *path) +void set_fs_root(struct fs_struct *fs, const struct path *path) { struct path old_root; @@ -29,7 +29,7 @@ void set_fs_root(struct fs_struct *fs, struct path *path) * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. * It can block. */ -void set_fs_pwd(struct fs_struct *fs, struct path *path) +void set_fs_pwd(struct fs_struct *fs, const struct path *path) { struct path old_pwd; @@ -53,7 +53,7 @@ static inline int replace_path(struct path *p, const struct path *old, const str return 1; } -void chroot_fs_refs(struct path *old_root, struct path *new_root) +void chroot_fs_refs(const struct path *old_root, const struct path *new_root) { struct task_struct *g, *p; struct fs_struct *fs; diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e9bdec0..11dfa0c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -532,7 +532,7 @@ void fuse_request_send_background_locked(struct fuse_conn *fc, void fuse_force_forget(struct file *file, u64 nodeid) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_forget_in inarg; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c807176..34b80ba 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -355,7 +355,7 @@ static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index) static int fuse_flush(struct file *file, fl_owner_t id) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; struct fuse_req *req; @@ -1215,7 +1215,7 @@ static ssize_t __fuse_direct_read(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos) { ssize_t res; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); if (is_bad_inode(inode)) return -EIO; @@ -1238,7 +1238,7 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, static ssize_t __fuse_direct_write(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); size_t count = iov_length(iov, nr_segs); ssize_t res; @@ -1258,7 +1258,7 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count }; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); ssize_t res; if (is_bad_inode(inode)) @@ -1485,7 +1485,7 @@ static const struct vm_operations_struct fuse_file_vm_ops = { static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) { if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_file *ff = file->private_data; @@ -1543,7 +1543,7 @@ static void fuse_lk_fill(struct fuse_req *req, struct file *file, const struct file_lock *fl, int opcode, pid_t pid, int flock) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; struct fuse_lk_in *arg = &req->misc.lk_in; @@ -1565,7 +1565,7 @@ static void fuse_lk_fill(struct fuse_req *req, struct file *file, static int fuse_getlk(struct file *file, struct file_lock *fl) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_lk_out outarg; @@ -1590,7 +1590,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl) static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK; @@ -1622,7 +1622,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); int err; @@ -1645,7 +1645,7 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); int err; @@ -1702,7 +1702,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block) static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) { loff_t retval; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */ if (whence == SEEK_CUR || whence == SEEK_SET) @@ -2079,7 +2079,7 @@ EXPORT_SYMBOL_GPL(fuse_do_ioctl); long fuse_ioctl_common(struct file *file, unsigned int cmd, unsigned long arg, unsigned int flags) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); if (!fuse_allow_current_process(fc)) diff --git a/fs/internal.h b/fs/internal.h index 2f6af7f..507141f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -69,7 +69,7 @@ extern void __mnt_drop_write_file(struct file *); /* * fs_struct.c */ -extern void chroot_fs_refs(struct path *, struct path *); +extern void chroot_fs_refs(const struct path *, const struct path *); /* * file_table.c @@ -451,7 +451,7 @@ int inode_permission(struct inode *inode, int mask) * * Given a path increment the reference count to the dentry and the vfsmount. */ -void path_get(struct path *path) +void path_get(const struct path *path) { mntget(path->mnt); dget(path->dentry); @@ -464,7 +464,7 @@ EXPORT_SYMBOL(path_get); * * Given a path decrement the reference count to the dentry and the vfsmount. */ -void path_put(struct path *path) +void path_put(const struct path *path) { dput(path->dentry); mntput(path->mnt); @@ -704,7 +704,7 @@ static int do_dentry_open(struct file *f, f->f_mode = FMODE_PATH; path_get(&f->f_path); - inode = file_inode(f); + inode = f->f_inode = f->f_path.dentry->d_inode; if (f->f_mode & FMODE_WRITE) { error = __get_file_write_access(inode, f->f_path.mnt); if (error) @@ -767,6 +767,7 @@ cleanup_file: path_put(&f->f_path); f->f_path.mnt = NULL; f->f_path.dentry = NULL; + f->f_inode = NULL; return error; } diff --git a/fs/seq_file.c b/fs/seq_file.c index 15c6304..38bb59f 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -339,7 +339,7 @@ EXPORT_SYMBOL(seq_lseek); /** * seq_release - free the structures associated with sequential file. * @file: file in question - * @inode: file->f_path.dentry->d_inode + * @inode: its inode * * Frees the structures associated with sequential file; can be used * as ->f_op->release() if you don't have private data to destroy. |