summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig8
-rw-r--r--fs/Makefile1
-rw-r--r--fs/befs/linuxvfs.c14
-rw-r--r--fs/binfmt_elf.c2
-rw-r--r--fs/cifs/cifsfs.c10
-rw-r--r--fs/compat_ioctl.c22
-rw-r--r--fs/crypto/crypto.c1
-rw-r--r--fs/crypto/fname.c140
-rw-r--r--fs/crypto/fscrypt_private.h31
-rw-r--r--fs/crypto/hooks.c158
-rw-r--r--fs/crypto/keyinfo.c17
-rw-r--r--fs/dax.c9
-rw-r--r--fs/dcache.c104
-rw-r--r--fs/exofs/super.c7
-rw-r--r--fs/ext2/file.c2
-rw-r--r--fs/ext2/super.c21
-rw-r--r--fs/ext4/acl.h2
-rw-r--r--fs/ext4/balloc.c4
-rw-r--r--fs/ext4/block_validity.c6
-rw-r--r--fs/ext4/ext4.h22
-rw-r--r--fs/ext4/ext4_extents.h14
-rw-r--r--fs/ext4/ext4_jbd2.h5
-rw-r--r--fs/ext4/extents.c14
-rw-r--r--fs/ext4/extents_status.h2
-rw-r--r--fs/ext4/file.c10
-rw-r--r--fs/ext4/fsmap.c15
-rw-r--r--fs/ext4/fsmap.h15
-rw-r--r--fs/ext4/hash.c6
-rw-r--r--fs/ext4/ialloc.c4
-rw-r--r--fs/ext4/inline.c10
-rw-r--r--fs/ext4/inode.c16
-rw-r--r--fs/ext4/mballoc.c28
-rw-r--r--fs/ext4/mballoc.h2
-rw-r--r--fs/ext4/migrate.c9
-rw-r--r--fs/ext4/move_extent.c10
-rw-r--r--fs/ext4/namei.c64
-rw-r--r--fs/ext4/resize.c2
-rw-r--r--fs/ext4/super.c34
-rw-r--r--fs/ext4/symlink.c43
-rw-r--r--fs/ext4/sysfs.c65
-rw-r--r--fs/ext4/truncate.h2
-rw-r--r--fs/ext4/xattr.h2
-rw-r--r--fs/f2fs/inode.c2
-rw-r--r--fs/f2fs/namei.c132
-rw-r--r--fs/fhandle.c3
-rw-r--r--fs/freevxfs/vxfs_super.c8
-rw-r--r--fs/gfs2/Kconfig3
-rw-r--r--fs/gfs2/glock.c65
-rw-r--r--fs/hfsplus/dir.c4
-rw-r--r--fs/hfsplus/hfsplus_fs.h3
-rw-r--r--fs/hfsplus/inode.c7
-rw-r--r--fs/hfsplus/super.c2
-rw-r--r--fs/inode.c1
-rw-r--r--fs/jbd2/checkpoint.c5
-rw-r--r--fs/jbd2/commit.c5
-rw-r--r--fs/jbd2/journal.c5
-rw-r--r--fs/jbd2/recovery.c5
-rw-r--r--fs/jbd2/revoke.c5
-rw-r--r--fs/jbd2/transaction.c10
-rw-r--r--fs/jfs/super.c8
-rw-r--r--fs/mbcache.c8
-rw-r--r--fs/ncpfs/Kconfig108
-rw-r--r--fs/ncpfs/Makefile17
-rw-r--r--fs/ncpfs/dir.c1232
-rw-r--r--fs/ncpfs/file.c263
-rw-r--r--fs/ncpfs/getopt.c76
-rw-r--r--fs/ncpfs/getopt.h17
-rw-r--r--fs/ncpfs/inode.c1066
-rw-r--r--fs/ncpfs/ioctl.c923
-rw-r--r--fs/ncpfs/mmap.c125
-rw-r--r--fs/ncpfs/ncp_fs.h101
-rw-r--r--fs/ncpfs/ncp_fs_i.h31
-rw-r--r--fs/ncpfs/ncp_fs_sb.h174
-rw-r--r--fs/ncpfs/ncplib_kernel.c1322
-rw-r--r--fs/ncpfs/ncplib_kernel.h215
-rw-r--r--fs/ncpfs/ncpsign_kernel.c128
-rw-r--r--fs/ncpfs/ncpsign_kernel.h27
-rw-r--r--fs/ncpfs/sock.c855
-rw-r--r--fs/ncpfs/symlink.c182
-rw-r--r--fs/nilfs2/segbuf.c2
-rw-r--r--fs/nilfs2/segbuf.h4
-rw-r--r--fs/nilfs2/segment.c2
-rw-r--r--fs/nilfs2/segment.h2
-rw-r--r--fs/nilfs2/sufile.c2
-rw-r--r--fs/nilfs2/sufile.h2
-rw-r--r--fs/nilfs2/super.c4
-rw-r--r--fs/nilfs2/sysfs.c21
-rw-r--r--fs/nilfs2/the_nilfs.h8
-rw-r--r--fs/orangefs/super.c15
-rw-r--r--fs/overlayfs/Kconfig31
-rw-r--r--fs/overlayfs/Makefile3
-rw-r--r--fs/overlayfs/copy_up.c188
-rw-r--r--fs/overlayfs/dir.c175
-rw-r--r--fs/overlayfs/export.c715
-rw-r--r--fs/overlayfs/inode.c106
-rw-r--r--fs/overlayfs/namei.c533
-rw-r--r--fs/overlayfs/overlayfs.h66
-rw-r--r--fs/overlayfs/ovl_entry.h11
-rw-r--r--fs/overlayfs/readdir.c57
-rw-r--r--fs/overlayfs/super.c125
-rw-r--r--fs/overlayfs/util.c108
-rw-r--r--fs/pipe.c57
-rw-r--r--fs/proc/array.c8
-rw-r--r--fs/proc/base.c70
-rw-r--r--fs/proc/consoles.c3
-rw-r--r--fs/proc/fd.c2
-rw-r--r--fs/proc/generic.c18
-rw-r--r--fs/proc/inode.c11
-rw-r--r--fs/proc/internal.h29
-rw-r--r--fs/proc/kcore.c18
-rw-r--r--fs/proc/proc_net.c4
-rw-r--r--fs/proc/self.c9
-rw-r--r--fs/proc/thread_self.c8
-rw-r--r--fs/proc/vmcore.c6
-rw-r--r--fs/pstore/platform.c1
-rw-r--r--fs/sysfs/dir.c9
-rw-r--r--fs/sysfs/file.c8
-rw-r--r--fs/sysfs/group.c6
-rw-r--r--fs/sysfs/mount.c5
-rw-r--r--fs/sysfs/symlink.c3
-rw-r--r--fs/sysfs/sysfs.h3
-rw-r--r--fs/ubifs/dir.c63
-rw-r--r--fs/ubifs/file.c36
-rw-r--r--fs/ubifs/super.c4
-rw-r--r--fs/udf/udftime.c5
-rw-r--r--fs/ufs/super.c13
-rw-r--r--fs/xfs/libxfs/xfs_sb.c4
-rw-r--r--fs/xfs/xfs_file.c2
-rw-r--r--fs/xfs/xfs_super.c24
-rw-r--r--fs/xfs/xfs_super.h7
130 files changed, 2596 insertions, 8116 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 0ed56752f208..bc821a86d965 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -58,6 +58,13 @@ config FS_DAX_PMD
depends on ZONE_DEVICE
depends on TRANSPARENT_HUGEPAGE
+# Selected by DAX drivers that do not expect filesystem DAX to support
+# get_user_pages() of DAX mappings. I.e. "limited" indicates no support
+# for fork() of processes with MAP_SHARED mappings or support for
+# direct-I/O to a DAX mapping.
+config FS_DAX_LIMITED
+ bool
+
endif # BLOCK
# Posix ACL utility routines
@@ -294,7 +301,6 @@ config NFS_COMMON
source "net/sunrpc/Kconfig"
source "fs/ceph/Kconfig"
source "fs/cifs/Kconfig"
-source "fs/ncpfs/Kconfig"
source "fs/coda/Kconfig"
source "fs/afs/Kconfig"
source "fs/9p/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index ef772f1eaff8..add789ea270a 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -92,7 +92,6 @@ obj-$(CONFIG_LOCKD) += lockd/
obj-$(CONFIG_NLS) += nls/
obj-$(CONFIG_SYSV_FS) += sysv/
obj-$(CONFIG_CIFS) += cifs/
-obj-$(CONFIG_NCP_FS) += ncpfs/
obj-$(CONFIG_HPFS_FS) += hpfs/
obj-$(CONFIG_NTFS_FS) += ntfs/
obj-$(CONFIG_UFS_FS) += ufs/
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index ee236231cafa..af2832aaeec5 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -444,11 +444,15 @@ unacquire_none:
static int __init
befs_init_inodecache(void)
{
- befs_inode_cachep = kmem_cache_create("befs_inode_cache",
- sizeof (struct befs_inode_info),
- 0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_ACCOUNT),
- init_once);
+ befs_inode_cachep = kmem_cache_create_usercopy("befs_inode_cache",
+ sizeof(struct befs_inode_info), 0,
+ (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT),
+ offsetof(struct befs_inode_info,
+ i_data.symlink),
+ sizeof_field(struct befs_inode_info,
+ i_data.symlink),
+ init_once);
if (befs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 83732fef510d..bdb201230bae 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1599,6 +1599,8 @@ static int fill_files_note(struct memelfnote *note)
/* *Estimated* file count and total data size needed */
count = current->mm->map_count;
+ if (count > UINT_MAX / 64)
+ return -EINVAL;
size = count * 64;
names_ofs = (2 + 3 * count) * sizeof(data[0]);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index a7be591d8e18..32cdea67bbfd 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1239,9 +1239,11 @@ cifs_init_request_bufs(void)
cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n",
CIFSMaxBufSize, CIFSMaxBufSize);
*/
- cifs_req_cachep = kmem_cache_create("cifs_request",
+ cifs_req_cachep = kmem_cache_create_usercopy("cifs_request",
CIFSMaxBufSize + max_hdr_size, 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ SLAB_HWCACHE_ALIGN, 0,
+ CIFSMaxBufSize + max_hdr_size,
+ NULL);
if (cifs_req_cachep == NULL)
return -ENOMEM;
@@ -1267,9 +1269,9 @@ cifs_init_request_bufs(void)
more SMBs to use small buffer alloc and is still much more
efficient to alloc 1 per page off the slab compared to 17K (5page)
alloc of large cifs buffers even when page debugging is on */
- cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
+ cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq",
MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
- NULL);
+ 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL);
if (cifs_sm_req_cachep == NULL) {
mempool_destroy(cifs_req_poolp);
kmem_cache_destroy(cifs_req_cachep);
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 5fc5dc660600..ef80085ed564 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -1218,23 +1218,11 @@ COMPATIBLE_IOCTL(DMX_SET_PES_FILTER)
COMPATIBLE_IOCTL(DMX_SET_BUFFER_SIZE)
COMPATIBLE_IOCTL(DMX_GET_PES_PIDS)
COMPATIBLE_IOCTL(DMX_GET_STC)
-COMPATIBLE_IOCTL(FE_GET_INFO)
-COMPATIBLE_IOCTL(FE_DISEQC_RESET_OVERLOAD)
-COMPATIBLE_IOCTL(FE_DISEQC_SEND_MASTER_CMD)
-COMPATIBLE_IOCTL(FE_DISEQC_RECV_SLAVE_REPLY)
-COMPATIBLE_IOCTL(FE_DISEQC_SEND_BURST)
-COMPATIBLE_IOCTL(FE_SET_TONE)
-COMPATIBLE_IOCTL(FE_SET_VOLTAGE)
-COMPATIBLE_IOCTL(FE_ENABLE_HIGH_LNB_VOLTAGE)
-COMPATIBLE_IOCTL(FE_READ_STATUS)
-COMPATIBLE_IOCTL(FE_READ_BER)
-COMPATIBLE_IOCTL(FE_READ_SIGNAL_STRENGTH)
-COMPATIBLE_IOCTL(FE_READ_SNR)
-COMPATIBLE_IOCTL(FE_READ_UNCORRECTED_BLOCKS)
-COMPATIBLE_IOCTL(FE_SET_FRONTEND)
-COMPATIBLE_IOCTL(FE_GET_FRONTEND)
-COMPATIBLE_IOCTL(FE_GET_EVENT)
-COMPATIBLE_IOCTL(FE_DISHNETWORK_SEND_LEGACY_CMD)
+COMPATIBLE_IOCTL(DMX_REQBUFS)
+COMPATIBLE_IOCTL(DMX_QUERYBUF)
+COMPATIBLE_IOCTL(DMX_EXPBUF)
+COMPATIBLE_IOCTL(DMX_QBUF)
+COMPATIBLE_IOCTL(DMX_DQBUF)
COMPATIBLE_IOCTL(VIDEO_STOP)
COMPATIBLE_IOCTL(VIDEO_PLAY)
COMPATIBLE_IOCTL(VIDEO_FREEZE)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 732a786cce9d..ce654526c0fb 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -27,6 +27,7 @@
#include <linux/dcache.h>
#include <linux/namei.h>
#include <crypto/aes.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
static unsigned int num_prealloc_crypto_pages = 32;
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 305541bcd108..e33f3d3c5ade 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -13,42 +13,46 @@
#include <linux/scatterlist.h>
#include <linux/ratelimit.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
+static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
+{
+ if (str->len == 1 && str->name[0] == '.')
+ return true;
+
+ if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
+ return true;
+
+ return false;
+}
+
/**
* fname_encrypt() - encrypt a filename
*
- * The caller must have allocated sufficient memory for the @oname string.
+ * The output buffer must be at least as large as the input buffer.
+ * Any extra space is filled with NUL padding before encryption.
*
* Return: 0 on success, -errno on failure
*/
-static int fname_encrypt(struct inode *inode,
- const struct qstr *iname, struct fscrypt_str *oname)
+int fname_encrypt(struct inode *inode, const struct qstr *iname,
+ u8 *out, unsigned int olen)
{
struct skcipher_request *req = NULL;
DECLARE_CRYPTO_WAIT(wait);
- struct fscrypt_info *ci = inode->i_crypt_info;
- struct crypto_skcipher *tfm = ci->ci_ctfm;
+ struct crypto_skcipher *tfm = inode->i_crypt_info->ci_ctfm;
int res = 0;
char iv[FS_CRYPTO_BLOCK_SIZE];
struct scatterlist sg;
- int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
- unsigned int lim;
- unsigned int cryptlen;
-
- lim = inode->i_sb->s_cop->max_namelen(inode);
- if (iname->len <= 0 || iname->len > lim)
- return -EIO;
/*
* Copy the filename to the output buffer for encrypting in-place and
* pad it with the needed number of NUL bytes.
*/
- cryptlen = max_t(unsigned int, iname->len, FS_CRYPTO_BLOCK_SIZE);
- cryptlen = round_up(cryptlen, padding);
- cryptlen = min(cryptlen, lim);
- memcpy(oname->name, iname->name, iname->len);
- memset(oname->name + iname->len, 0, cryptlen - iname->len);
+ if (WARN_ON(olen < iname->len))
+ return -ENOBUFS;
+ memcpy(out, iname->name, iname->len);
+ memset(out + iname->len, 0, olen - iname->len);
/* Initialize the IV */
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
@@ -63,8 +67,8 @@ static int fname_encrypt(struct inode *inode,
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &wait);
- sg_init_one(&sg, oname->name, cryptlen);
- skcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv);
+ sg_init_one(&sg, out, olen);
+ skcipher_request_set_crypt(req, &sg, &sg, olen, iv);
/* Do the encryption */
res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
@@ -75,7 +79,6 @@ static int fname_encrypt(struct inode *inode,
return res;
}
- oname->len = cryptlen;
return 0;
}
@@ -188,50 +191,52 @@ static int digest_decode(const char *src, int len, char *dst)
return cp - dst;
}
-u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen)
+bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+ u32 max_len, u32 *encrypted_len_ret)
{
- int padding = 32;
- struct fscrypt_info *ci = inode->i_crypt_info;
-
- if (ci)
- padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
- ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE);
- return round_up(ilen, padding);
+ int padding = 4 << (inode->i_crypt_info->ci_flags &
+ FS_POLICY_FLAGS_PAD_MASK);
+ u32 encrypted_len;
+
+ if (orig_len > max_len)
+ return false;
+ encrypted_len = max(orig_len, (u32)FS_CRYPTO_BLOCK_SIZE);
+ encrypted_len = round_up(encrypted_len, padding);
+ *encrypted_len_ret = min(encrypted_len, max_len);
+ return true;
}
-EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
/**
- * fscrypt_fname_crypto_alloc_obuff() -
+ * fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames
+ *
+ * Allocate a buffer that is large enough to hold any decrypted or encoded
+ * filename (null-terminated), for the given maximum encrypted filename length.
*
- * Allocates an output buffer that is sufficient for the crypto operation
- * specified by the context and the direction.
+ * Return: 0 on success, -errno on failure
*/
int fscrypt_fname_alloc_buffer(const struct inode *inode,
- u32 ilen, struct fscrypt_str *crypto_str)
+ u32 max_encrypted_len,
+ struct fscrypt_str *crypto_str)
{
- u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
const u32 max_encoded_len =
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
+ u32 max_presented_len;
- crypto_str->len = olen;
- olen = max(olen, max_encoded_len);
+ max_presented_len = max(max_encoded_len, max_encrypted_len);
- /*
- * Allocated buffer can hold one more character to null-terminate the
- * string
- */
- crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
- if (!(crypto_str->name))
+ crypto_str->name = kmalloc(max_presented_len + 1, GFP_NOFS);
+ if (!crypto_str->name)
return -ENOMEM;
+ crypto_str->len = max_presented_len;
return 0;
}
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
/**
- * fscrypt_fname_crypto_free_buffer() -
+ * fscrypt_fname_free_buffer - free the buffer for presented filenames
*
- * Frees the buffer allocated for crypto operation.
+ * Free the buffer allocated by fscrypt_fname_alloc_buffer().
*/
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
{
@@ -298,35 +303,6 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
/**
- * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
- * space
- *
- * The caller must have allocated sufficient memory for the @oname string.
- *
- * Return: 0 on success, -errno on failure
- */
-int fscrypt_fname_usr_to_disk(struct inode *inode,
- const struct qstr *iname,
- struct fscrypt_str *oname)
-{
- if (fscrypt_is_dot_dotdot(iname)) {
- oname->name[0] = '.';
- oname->name[iname->len - 1] = '.';
- oname->len = iname->len;
- return 0;
- }
- if (inode->i_crypt_info)
- return fname_encrypt(inode, iname, oname);
- /*
- * Without a proper key, a user is not allowed to modify the filenames
- * in a directory. Consequently, a user space name cannot be mapped to
- * a disk-space name
- */
- return -ENOKEY;
-}
-EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
-
-/**
* fscrypt_setup_filename() - prepare to search a possibly encrypted directory
* @dir: the directory that will be searched
* @iname: the user-provided filename being searched for
@@ -369,11 +345,17 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
return ret;
if (dir->i_crypt_info) {
- ret = fscrypt_fname_alloc_buffer(dir, iname->len,
- &fname->crypto_buf);
- if (ret)
- return ret;
- ret = fname_encrypt(dir, iname, &fname->crypto_buf);
+ if (!fscrypt_fname_encrypted_size(dir, iname->len,
+ dir->i_sb->s_cop->max_namelen(dir),
+ &fname->crypto_buf.len))
+ return -ENAMETOOLONG;
+ fname->crypto_buf.name = kmalloc(fname->crypto_buf.len,
+ GFP_NOFS);
+ if (!fname->crypto_buf.name)
+ return -ENOMEM;
+
+ ret = fname_encrypt(dir, iname, fname->crypto_buf.name,
+ fname->crypto_buf.len);
if (ret)
goto errout;
fname->disk_name.name = fname->crypto_buf.name;
@@ -425,7 +407,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
return 0;
errout:
- fscrypt_fname_free_buffer(&fname->crypto_buf);
+ kfree(fname->crypto_buf.name);
return ret;
}
EXPORT_SYMBOL(fscrypt_setup_filename);
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index c0b4f5597e1a..ad6722bae8b7 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -50,6 +50,15 @@ struct fscrypt_context {
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
+/**
+ * For encrypted symlinks, the ciphertext length is stored at the beginning
+ * of the string in little-endian format.
+ */
+struct fscrypt_symlink_data {
+ __le16 len;
+ char encrypted_path[1];
+} __packed;
+
/*
* A pointer to this structure is stored in the file system's in-core
* representation of an inode.
@@ -71,7 +80,22 @@ typedef enum {
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002
+static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
+ u32 filenames_mode)
+{
+ if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
+ filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
+ return true;
+
+ if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
+ filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
+ return true;
+
+ return false;
+}
+
/* crypto.c */
+extern struct kmem_cache *fscrypt_info_cachep;
extern int fscrypt_initialize(unsigned int cop_flags);
extern struct workqueue_struct *fscrypt_read_workqueue;
extern int fscrypt_do_page_crypto(const struct inode *inode,
@@ -83,6 +107,13 @@ extern int fscrypt_do_page_crypto(const struct inode *inode,
extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
gfp_t gfp_flags);
+/* fname.c */
+extern int fname_encrypt(struct inode *inode, const struct qstr *iname,
+ u8 *out, unsigned int olen);
+extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
+ u32 orig_len, u32 max_len,
+ u32 *encrypted_len_ret);
+
/* keyinfo.c */
extern void __exit fscrypt_essiv_cleanup(void);
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
index 9f5fb2eb9cf7..bec06490fb13 100644
--- a/fs/crypto/hooks.c
+++ b/fs/crypto/hooks.c
@@ -110,3 +110,161 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry)
return 0;
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
+
+int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link)
+{
+ int err;
+
+ /*
+ * To calculate the size of the encrypted symlink target we need to know
+ * the amount of NUL padding, which is determined by the flags set in
+ * the encryption policy which will be inherited from the directory.
+ * The easiest way to get access to this is to just load the directory's
+ * fscrypt_info, since we'll need it to create the dir_entry anyway.
+ *
+ * Note: in test_dummy_encryption mode, @dir may be unencrypted.
+ */
+ err = fscrypt_get_encryption_info(dir);
+ if (err)
+ return err;
+ if (!fscrypt_has_encryption_key(dir))
+ return -ENOKEY;
+
+ /*
+ * Calculate the size of the encrypted symlink and verify it won't
+ * exceed max_len. Note that for historical reasons, encrypted symlink
+ * targets are prefixed with the ciphertext length, despite this
+ * actually being redundant with i_size. This decreases by 2 bytes the
+ * longest symlink target we can accept.
+ *
+ * We could recover 1 byte by not counting a null terminator, but
+ * counting it (even though it is meaningless for ciphertext) is simpler
+ * for now since filesystems will assume it is there and subtract it.
+ */
+ if (!fscrypt_fname_encrypted_size(dir, len,
+ max_len - sizeof(struct fscrypt_symlink_data),
+ &disk_link->len))
+ return -ENAMETOOLONG;
+ disk_link->len += sizeof(struct fscrypt_symlink_data);
+
+ disk_link->name = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fscrypt_prepare_symlink);
+
+int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
+ unsigned int len, struct fscrypt_str *disk_link)
+{
+ int err;
+ struct qstr iname = QSTR_INIT(target, len);
+ struct fscrypt_symlink_data *sd;
+ unsigned int ciphertext_len;
+
+ err = fscrypt_require_key(inode);
+ if (err)
+ return err;
+
+ if (disk_link->name) {
+ /* filesystem-provided buffer */
+ sd = (struct fscrypt_symlink_data *)disk_link->name;
+ } else {
+ sd = kmalloc(disk_link->len, GFP_NOFS);
+ if (!sd)
+ return -ENOMEM;
+ }
+ ciphertext_len = disk_link->len - sizeof(*sd);
+ sd->len = cpu_to_le16(ciphertext_len);
+
+ err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
+ if (err) {
+ if (!disk_link->name)
+ kfree(sd);
+ return err;
+ }
+ /*
+ * Null-terminating the ciphertext doesn't make sense, but we still
+ * count the null terminator in the length, so we might as well
+ * initialize it just in case the filesystem writes it out.
+ */
+ sd->encrypted_path[ciphertext_len] = '\0';
+
+ if (!disk_link->name)
+ disk_link->name = (unsigned char *)sd;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
+
+/**
+ * fscrypt_get_symlink - get the target of an encrypted symlink
+ * @inode: the symlink inode
+ * @caddr: the on-disk contents of the symlink
+ * @max_size: size of @caddr buffer
+ * @done: if successful, will be set up to free the returned target
+ *
+ * If the symlink's encryption key is available, we decrypt its target.
+ * Otherwise, we encode its target for presentation.
+ *
+ * This may sleep, so the filesystem must have dropped out of RCU mode already.
+ *
+ * Return: the presentable symlink target or an ERR_PTR()
+ */
+const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+ unsigned int max_size,
+ struct delayed_call *done)
+{
+ const struct fscrypt_symlink_data *sd;
+ struct fscrypt_str cstr, pstr;
+ int err;
+
+ /* This is for encrypted symlinks only */
+ if (WARN_ON(!IS_ENCRYPTED(inode)))
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Try to set up the symlink's encryption key, but we can continue
+ * regardless of whether the key is available or not.
+ */
+ err = fscrypt_get_encryption_info(inode);
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+ * For historical reasons, encrypted symlink targets are prefixed with
+ * the ciphertext length, even though this is redundant with i_size.
+ */
+
+ if (max_size < sizeof(*sd))
+ return ERR_PTR(-EUCLEAN);
+ sd = caddr;
+ cstr.name = (unsigned char *)sd->encrypted_path;
+ cstr.len = le16_to_cpu(sd->len);
+
+ if (cstr.len == 0)
+ return ERR_PTR(-EUCLEAN);
+
+ if (cstr.len + sizeof(*sd) - 1 > max_size)
+ return ERR_PTR(-EUCLEAN);
+
+ err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
+ if (err)
+ return ERR_PTR(err);
+
+ err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
+ if (err)
+ goto err_kfree;
+
+ err = -EUCLEAN;
+ if (pstr.name[0] == '\0')
+ goto err_kfree;
+
+ pstr.name[pstr.len] = '\0';
+ set_delayed_call(done, kfree_link, pstr.name);
+ return pstr.name;
+
+err_kfree:
+ kfree(pstr.name);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(fscrypt_get_symlink);
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index 5e6e846f5a24..05f5ee1f0705 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -14,6 +14,7 @@
#include <linux/ratelimit.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
static struct crypto_shash *essiv_hash_tfm;
@@ -354,19 +355,9 @@ out:
}
EXPORT_SYMBOL(fscrypt_get_encryption_info);
-void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
+void fscrypt_put_encryption_info(struct inode *inode)
{
- struct fscrypt_info *prev;
-
- if (ci == NULL)
- ci = READ_ONCE(inode->i_crypt_info);
- if (ci == NULL)
- return;
-
- prev = cmpxchg(&inode->i_crypt_info, ci, NULL);
- if (prev != ci)
- return;
-
- put_crypt_info(ci);
+ put_crypt_info(inode->i_crypt_info);
+ inode->i_crypt_info = NULL;
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);
diff --git a/fs/dax.c b/fs/dax.c
index 6ee6f7e24f5a..0276df90e86c 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -1095,7 +1095,7 @@ static bool dax_fault_is_synchronous(unsigned long flags,
}
static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp,
- const struct iomap_ops *ops)
+ int *iomap_errp, const struct iomap_ops *ops)
{
struct vm_area_struct *vma = vmf->vma;
struct address_space *mapping = vma->vm_file->f_mapping;
@@ -1148,6 +1148,8 @@ static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp,
* that we never have to deal with more than a single extent here.
*/
error = ops->iomap_begin(inode, pos, PAGE_SIZE, flags, &iomap);
+ if (iomap_errp)
+ *iomap_errp = error;
if (error) {
vmf_ret = dax_fault_return(error);
goto unlock_entry;
@@ -1481,6 +1483,7 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp,
* @vmf: The description of the fault
* @pe_size: Size of the page to fault in
* @pfnp: PFN to insert for synchronous faults if fsync is required
+ * @iomap_errp: Storage for detailed error code in case of error
* @ops: Iomap ops passed from the file system
*
* When a page fault occurs, filesystems may call this helper in
@@ -1489,11 +1492,11 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp,
* successfully.
*/
int dax_iomap_fault(struct vm_fault *vmf, enum page_entry_size pe_size,
- pfn_t *pfnp, const struct iomap_ops *ops)
+ pfn_t *pfnp, int *iomap_errp, const struct iomap_ops *ops)
{
switch (pe_size) {
case PE_SIZE_PTE:
- return dax_iomap_pte_fault(vmf, pfnp, ops);
+ return dax_iomap_pte_fault(vmf, pfnp, iomap_errp, ops);
case PE_SIZE_PMD:
return dax_iomap_pmd_fault(vmf, pfnp, ops);
default:
diff --git a/fs/dcache.c b/fs/dcache.c
index d93872d425d6..7c38f39958bc 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -37,8 +37,6 @@
#include <linux/prefetch.h>
#include <linux/ratelimit.h>
#include <linux/list_lru.h>
-#include <linux/kasan.h>
-
#include "internal.h"
#include "mount.h"
@@ -193,7 +191,7 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
unsigned long a,b,mask;
for (;;) {
- a = *(unsigned long *)cs;
+ a = read_word_at_a_time(cs);
b = load_unaligned_zeropad(ct);
if (tcount < sizeof(unsigned long))
break;
@@ -1628,9 +1626,6 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
}
atomic_set(&p->u.count, 1);
dname = p->name;
- if (IS_ENABLED(CONFIG_DCACHE_WORD_ACCESS))
- kasan_unpoison_shadow(dname,
- round_up(name->len + 1, sizeof(unsigned long)));
} else {
dname = dentry->d_iname;
}
@@ -1703,9 +1698,15 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
}
EXPORT_SYMBOL(d_alloc);
+struct dentry *d_alloc_anon(struct super_block *sb)
+{
+ return __d_alloc(sb, NULL);
+}
+EXPORT_SYMBOL(d_alloc_anon);
+
struct dentry *d_alloc_cursor(struct dentry * parent)
{
- struct dentry *dentry = __d_alloc(parent->d_sb, NULL);
+ struct dentry *dentry = d_alloc_anon(parent->d_sb);
if (dentry) {
dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR;
dentry->d_parent = dget(parent);
@@ -1891,7 +1892,7 @@ struct dentry *d_make_root(struct inode *root_inode)
struct dentry *res = NULL;
if (root_inode) {
- res = __d_alloc(root_inode->i_sb, NULL);
+ res = d_alloc_anon(root_inode->i_sb);
if (res)
d_instantiate(res, root_inode);
else
@@ -1930,33 +1931,19 @@ struct dentry *d_find_any_alias(struct inode *inode)
}
EXPORT_SYMBOL(d_find_any_alias);
-static struct dentry *__d_obtain_alias(struct inode *inode, int disconnected)
+static struct dentry *__d_instantiate_anon(struct dentry *dentry,
+ struct inode *inode,
+ bool disconnected)
{
- struct dentry *tmp;
struct dentry *res;
unsigned add_flags;
- if (!inode)
- return ERR_PTR(-ESTALE);
- if (IS_ERR(inode))
- return ERR_CAST(inode);
-
- res = d_find_any_alias(inode);
- if (res)
- goto out_iput;
-
- tmp = __d_alloc(inode->i_sb, NULL);
- if (!tmp) {
- res = ERR_PTR(-ENOMEM);
- goto out_iput;
- }
-
- security_d_instantiate(tmp, inode);
+ security_d_instantiate(dentry, inode);
spin_lock(&inode->i_lock);
res = __d_find_any_alias(inode);
if (res) {
spin_unlock(&inode->i_lock);
- dput(tmp);
+ dput(dentry);
goto out_iput;
}
@@ -1966,24 +1953,57 @@ static struct dentry *__d_obtain_alias(struct inode *inode, int disconnected)
if (disconnected)
add_flags |= DCACHE_DISCONNECTED;
- spin_lock(&tmp->d_lock);
- __d_set_inode_and_type(tmp, inode, add_flags);
- hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry);
+ spin_lock(&dentry->d_lock);
+ __d_set_inode_and_type(dentry, inode, add_flags);
+ hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
if (!disconnected) {
- hlist_bl_lock(&tmp->d_sb->s_roots);
- hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_roots);
- hlist_bl_unlock(&tmp->d_sb->s_roots);
+ hlist_bl_lock(&dentry->d_sb->s_roots);
+ hlist_bl_add_head(&dentry->d_hash, &dentry->d_sb->s_roots);
+ hlist_bl_unlock(&dentry->d_sb->s_roots);
}
- spin_unlock(&tmp->d_lock);
+ spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock);
- return tmp;
+ return dentry;
out_iput:
iput(inode);
return res;
}
+struct dentry *d_instantiate_anon(struct dentry *dentry, struct inode *inode)
+{
+ return __d_instantiate_anon(dentry, inode, true);
+}
+EXPORT_SYMBOL(d_instantiate_anon);
+
+static struct dentry *__d_obtain_alias(struct inode *inode, bool disconnected)
+{
+ struct dentry *tmp;
+ struct dentry *res;
+
+ if (!inode)
+ return ERR_PTR(-ESTALE);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+
+ res = d_find_any_alias(inode);
+ if (res)
+ goto out_iput;
+
+ tmp = d_alloc_anon(inode->i_sb);
+ if (!tmp) {
+ res = ERR_PTR(-ENOMEM);
+ goto out_iput;
+ }
+
+ return __d_instantiate_anon(tmp, inode, disconnected);
+
+out_iput:
+ iput(inode);
+ return res;
+}
+
/**
* d_obtain_alias - find or allocate a DISCONNECTED dentry for a given inode
* @inode: inode to allocate the dentry for
@@ -2004,7 +2024,7 @@ static struct dentry *__d_obtain_alias(struct inode *inode, int disconnected)
*/
struct dentry *d_obtain_alias(struct inode *inode)
{
- return __d_obtain_alias(inode, 1);
+ return __d_obtain_alias(inode, true);
}
EXPORT_SYMBOL(d_obtain_alias);
@@ -2025,7 +2045,7 @@ EXPORT_SYMBOL(d_obtain_alias);
*/
struct dentry *d_obtain_root(struct inode *inode)
{
- return __d_obtain_alias(inode, 0);
+ return __d_obtain_alias(inode, false);
}
EXPORT_SYMBOL(d_obtain_root);
@@ -3532,6 +3552,7 @@ bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
return result;
}
+EXPORT_SYMBOL(is_subdir);
static enum d_walk_ret d_genocide_kill(void *data, struct dentry *dentry)
{
@@ -3607,8 +3628,9 @@ static void __init dcache_init(void)
* but it is probably not worth it because of the cache nature
* of the dcache.
*/
- dentry_cache = KMEM_CACHE(dentry,
- SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT);
+ dentry_cache = KMEM_CACHE_USERCOPY(dentry,
+ SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
+ d_iname);
/* Hash may have been set up in dcache_init_early */
if (!hashdist)
@@ -3646,8 +3668,8 @@ void __init vfs_caches_init_early(void)
void __init vfs_caches_init(void)
{
- names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
dcache_init();
inode_init();
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index 7e244093c0e5..179cd5c2f52a 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -193,10 +193,13 @@ static void exofs_init_once(void *foo)
*/
static int init_inodecache(void)
{
- exofs_inode_cachep = kmem_cache_create("exofs_inode_cache",
+ exofs_inode_cachep = kmem_cache_create_usercopy("exofs_inode_cache",
sizeof(struct exofs_i_info), 0,
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD |
- SLAB_ACCOUNT, exofs_init_once);
+ SLAB_ACCOUNT,
+ offsetof(struct exofs_i_info, i_data),
+ sizeof_field(struct exofs_i_info, i_data),
+ exofs_init_once);
if (exofs_inode_cachep == NULL)
return -ENOMEM;
return 0;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 2da67699dc33..09640220fda8 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -100,7 +100,7 @@ static int ext2_dax_fault(struct vm_fault *vmf)
}
down_read(&ei->dax_sem);
- ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, &ext2_iomap_ops);
+ ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, NULL, &ext2_iomap_ops);
up_read(&ei->dax_sem);
if (vmf->flags & FAULT_FLAG_WRITE)
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 554c98b8a93a..7666c065b96f 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -221,11 +221,13 @@ static void init_once(void *foo)
static int __init init_inodecache(void)
{
- ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",
- sizeof(struct ext2_inode_info),
- 0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_ACCOUNT),
- init_once);
+ ext2_inode_cachep = kmem_cache_create_usercopy("ext2_inode_cache",
+ sizeof(struct ext2_inode_info), 0,
+ (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT),
+ offsetof(struct ext2_inode_info, i_data),
+ sizeof_field(struct ext2_inode_info, i_data),
+ init_once);
if (ext2_inode_cachep == NULL)
return -ENOMEM;
return 0;
@@ -960,8 +962,11 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
if (sbi->s_mount_opt & EXT2_MOUNT_DAX) {
err = bdev_dax_supported(sb, blocksize);
- if (err)
- goto failed_mount;
+ if (err) {
+ ext2_msg(sb, KERN_ERR,
+ "DAX unsupported by block device. Turning off DAX.");
+ sbi->s_mount_opt &= ~EXT2_MOUNT_DAX;
+ }
}
/* If the blocksize doesn't match, re-read the thing.. */
@@ -1226,7 +1231,7 @@ static void ext2_clear_super_error(struct super_block *sb)
* write and hope for the best.
*/
ext2_msg(sb, KERN_ERR,
- "previous I/O error to superblock detected\n");
+ "previous I/O error to superblock detected");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index a48fc5ae2701..9b63f5416a2f 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
File: fs/ext4/acl.h
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index a943e568292e..f9b3e0a83526 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -355,10 +355,10 @@ static ext4_fsblk_t ext4_valid_block_bitmap(struct super_block *sb,
blk = ext4_inode_table(sb, desc);
offset = blk - group_first_block;
next_zero_bit = ext4_find_next_zero_bit(bh->b_data,
- EXT4_B2C(sbi, offset + EXT4_SB(sb)->s_itb_per_group),
+ EXT4_B2C(sbi, offset + sbi->s_itb_per_group),
EXT4_B2C(sbi, offset));
if (next_zero_bit <
- EXT4_B2C(sbi, offset + EXT4_SB(sb)->s_itb_per_group))
+ EXT4_B2C(sbi, offset + sbi->s_itb_per_group))
/* bad bitmap for inode tables */
return blk;
return 0;
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index bee888e0e2db..913061c0de1b 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -147,11 +147,11 @@ int ext4_setup_system_zone(struct super_block *sb)
int ret;
if (!test_opt(sb, BLOCK_VALIDITY)) {
- if (EXT4_SB(sb)->system_blks.rb_node)
+ if (sbi->system_blks.rb_node)
ext4_release_system_zone(sb);
return 0;
}
- if (EXT4_SB(sb)->system_blks.rb_node)
+ if (sbi->system_blks.rb_node)
return 0;
for (i=0; i < ngroups; i++) {
@@ -173,7 +173,7 @@ int ext4_setup_system_zone(struct super_block *sb)
}
if (test_opt(sb, DEBUG))
- debug_print_tree(EXT4_SB(sb));
+ debug_print_tree(sbi);
return 0;
}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 4e091eae38b1..3241475a1733 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
* ext4.h
*
@@ -611,10 +611,10 @@ enum {
/*
* Flags used by ext4_free_blocks
*/
-#define EXT4_FREE_BLOCKS_METADATA 0x0001
-#define EXT4_FREE_BLOCKS_FORGET 0x0002
-#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
-#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008
+#define EXT4_FREE_BLOCKS_METADATA 0x0001
+#define EXT4_FREE_BLOCKS_FORGET 0x0002
+#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
+#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008
#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010
#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020
@@ -1986,10 +1986,10 @@ static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
/* Legal values for the dx_root hash_version field: */
-#define DX_HASH_LEGACY 0
-#define DX_HASH_HALF_MD4 1
-#define DX_HASH_TEA 2
-#define DX_HASH_LEGACY_UNSIGNED 3
+#define DX_HASH_LEGACY 0
+#define DX_HASH_HALF_MD4 1
+#define DX_HASH_TEA 2
+#define DX_HASH_LEGACY_UNSIGNED 3
#define DX_HASH_HALF_MD4_UNSIGNED 4
#define DX_HASH_TEA_UNSIGNED 5
@@ -2000,7 +2000,6 @@ static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc,
struct shash_desc shash;
char ctx[4];
} desc;
- int err;
BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver)!=sizeof(desc.ctx));
@@ -2008,8 +2007,7 @@ static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc,
desc.shash.flags = 0;
*(u32 *)desc.ctx = crc;
- err = crypto_shash_update(&desc.shash, address, length);
- BUG_ON(err);
+ BUG_ON(crypto_shash_update(&desc.shash, address, length));
return *(u32 *)desc.ctx;
}
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h
index 8ecf84b8f5a1..98fb0c119c68 100644
--- a/fs/ext4/ext4_extents.h
+++ b/fs/ext4/ext4_extents.h
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com
* Written by Alex Tomas <alex@clusterfs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
*/
#ifndef _EXT4_EXTENTS
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 48143e32411c..15b6dd733780 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ext4_jbd2.h
*
@@ -5,10 +6,6 @@
*
* Copyright 1998--1999 Red Hat corp --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Ext4-specific journaling extensions.
*/
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c941251ac0c0..054416e9d827 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com
* Written by Alex Tomas <alex@clusterfs.com>
@@ -5,19 +6,6 @@
* Architecture independence:
* Copyright (c) 2005, Bull S.A.
* Written by Pierre Peiffer <pierre.peiffer@bull.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
*/
/*
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index ca90fc96f47e..8efdeb903d6b 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/ext4/extents_status.h
*
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index a0ae27b1bc66..fb6f023622fe 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -280,7 +280,8 @@ out:
static int ext4_dax_huge_fault(struct vm_fault *vmf,
enum page_entry_size pe_size)
{
- int result;
+ int result, error = 0;
+ int retries = 0;
handle_t *handle = NULL;
struct inode *inode = file_inode(vmf->vma->vm_file);
struct super_block *sb = inode->i_sb;
@@ -304,6 +305,7 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
sb_start_pagefault(sb);
file_update_time(vmf->vma->vm_file);
down_read(&EXT4_I(inode)->i_mmap_sem);
+retry:
handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE,
EXT4_DATA_TRANS_BLOCKS(sb));
if (IS_ERR(handle)) {
@@ -314,9 +316,13 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
} else {
down_read(&EXT4_I(inode)->i_mmap_sem);
}
- result = dax_iomap_fault(vmf, pe_size, &pfn, &ext4_iomap_ops);
+ result = dax_iomap_fault(vmf, pe_size, &pfn, &error, &ext4_iomap_ops);
if (write) {
ext4_journal_stop(handle);
+
+ if ((result & VM_FAULT_ERROR) && error == -ENOSPC &&
+ ext4_should_retry_alloc(sb, &retries))
+ goto retry;
/* Handling synchronous page fault? */
if (result & VM_FAULT_NEEDDSYNC)
result = dax_finish_sync_fault(vmf, pe_size, pfn);
diff --git a/fs/ext4/fsmap.c b/fs/ext4/fsmap.c
index 7ec340898598..e871c4bf18e9 100644
--- a/fs/ext4/fsmap.c
+++ b/fs/ext4/fsmap.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ext4.h"
#include <linux/fsmap.h>
diff --git a/fs/ext4/fsmap.h b/fs/ext4/fsmap.h
index 9a2cd367cc66..68c8001fee85 100644
--- a/fs/ext4/fsmap.h
+++ b/fs/ext4/fsmap.h
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __EXT4_FSMAP_H__
#define __EXT4_FSMAP_H__
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c
index 00c6dd29e621..e22dcfab308b 100644
--- a/fs/ext4/hash.c
+++ b/fs/ext4/hash.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/ext4/hash.c
*
* Copyright (C) 2002 by Theodore Ts'o
- *
- * This file is released under the GPL v2.
- *
- * This file may be redistributed under the terms of the GNU Public
- * License.
*/
#include <linux/fs.h>
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index b32cf263750d..7830d28df331 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -303,7 +303,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
/* Do this BEFORE marking the inode not in use or returning an error */
ext4_clear_inode(inode);
- es = EXT4_SB(sb)->s_es;
+ es = sbi->s_es;
if (ino < EXT4_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
ext4_error(sb, "reserved or nonexistent inode %lu", ino);
goto error_return;
@@ -1157,7 +1157,7 @@ got:
ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
ext4_set_inode_state(inode, EXT4_STATE_NEW);
- ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
+ ei->i_extra_isize = sbi->s_want_extra_isize;
ei->i_inline_off = 0;
if (ext4_has_feature_inline_data(sb))
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index adfc1f360dae..70cf4c7b268a 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (c) 2012 Taobao.
* Written by Tao Ma <boyu.mt@taobao.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/iomap.h>
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 0eff5b761c6e..c94780075b04 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3768,10 +3768,18 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter)
/* Credits for sb + inode write */
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
if (IS_ERR(handle)) {
- /* This is really bad luck. We've written the data
- * but cannot extend i_size. Bail out and pretend
- * the write failed... */
- ret = PTR_ERR(handle);
+ /*
+ * We wrote the data but cannot extend
+ * i_size. Bail out. In async io case, we do
+ * not return error here because we have
+ * already submmitted the corresponding
+ * bio. Returning error here makes the caller
+ * think that this IO is done and failed
+ * resulting in race with bio's completion
+ * handler.
+ */
+ if (!ret)
+ ret = PTR_ERR(handle);
if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index d9f8b90a93ed..769a62708b1c 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com
* Written by Alex Tomas <alex@clusterfs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
*/
@@ -769,10 +757,10 @@ void ext4_mb_generate_buddy(struct super_block *sb,
clear_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &(grp->bb_state));
period = get_cycles() - period;
- spin_lock(&EXT4_SB(sb)->s_bal_lock);
- EXT4_SB(sb)->s_mb_buddies_generated++;
- EXT4_SB(sb)->s_mb_generation_time += period;
- spin_unlock(&EXT4_SB(sb)->s_bal_lock);
+ spin_lock(&sbi->s_bal_lock);
+ sbi->s_mb_buddies_generated++;
+ sbi->s_mb_generation_time += period;
+ spin_unlock(&sbi->s_bal_lock);
}
static void mb_regenerate_buddy(struct ext4_buddy *e4b)
@@ -1459,7 +1447,7 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
ext4_fsblk_t blocknr;
blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
- blocknr += EXT4_C2B(EXT4_SB(sb), block);
+ blocknr += EXT4_C2B(sbi, block);
ext4_grp_locked_error(sb, e4b->bd_group,
inode ? inode->i_ino : 0,
blocknr,
@@ -4850,9 +4838,9 @@ do_more:
if (in_range(ext4_block_bitmap(sb, gdp), block, count) ||
in_range(ext4_inode_bitmap(sb, gdp), block, count) ||
in_range(block, ext4_inode_table(sb, gdp),
- EXT4_SB(sb)->s_itb_per_group) ||
+ sbi->s_itb_per_group) ||
in_range(block + count - 1, ext4_inode_table(sb, gdp),
- EXT4_SB(sb)->s_itb_per_group)) {
+ sbi->s_itb_per_group)) {
ext4_error(sb, "Freeing blocks in system zone - "
"Block = %llu, count = %lu", block, count);
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index dcf52540f379..88c98f17e3d9 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/ext4/mballoc.h
*
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index cf5181b62df1..61a9d1927817 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -1,15 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright IBM Corporation, 2007
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
*/
#include <linux/slab.h>
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 9bb36909ec92..b96e4bd3b3ec 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (c) 2008,2009 NEC Software Tohoku, Ltd.
* Written by Takashi Sato <t-sato@yk.jp.nec.com>
* Akira Fujita <a-fujita@rs.jp.nec.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/fs.h>
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 6660686e505a..b1f21e3a0763 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3057,39 +3057,19 @@ static int ext4_symlink(struct inode *dir,
struct inode *inode;
int err, len = strlen(symname);
int credits;
- bool encryption_required;
struct fscrypt_str disk_link;
- struct fscrypt_symlink_data *sd = NULL;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
- disk_link.len = len + 1;
- disk_link.name = (char *) symname;
-
- encryption_required = (ext4_encrypted_inode(dir) ||
- DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)));
- if (encryption_required) {
- err = fscrypt_get_encryption_info(dir);
- if (err)
- return err;
- if (!fscrypt_has_encryption_key(dir))
- return -ENOKEY;
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
- sizeof(struct fscrypt_symlink_data));
- sd = kzalloc(disk_link.len, GFP_KERNEL);
- if (!sd)
- return -ENOMEM;
- }
-
- if (disk_link.len > dir->i_sb->s_blocksize) {
- err = -ENAMETOOLONG;
- goto err_free_sd;
- }
+ err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
+ &disk_link);
+ if (err)
+ return err;
err = dquot_initialize(dir);
if (err)
- goto err_free_sd;
+ return err;
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
/*
@@ -3118,27 +3098,18 @@ static int ext4_symlink(struct inode *dir,
if (IS_ERR(inode)) {
if (handle)
ext4_journal_stop(handle);
- err = PTR_ERR(inode);
- goto err_free_sd;
+ return PTR_ERR(inode);
}
- if (encryption_required) {
- struct qstr istr;
- struct fscrypt_str ostr =
- FSTR_INIT(sd->encrypted_path, disk_link.len);
-
- istr.name = (const unsigned char *) symname;
- istr.len = len;
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
+ if (IS_ENCRYPTED(inode)) {
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
if (err)
goto err_drop_inode;
- sd->len = cpu_to_le16(ostr.len);
- disk_link.name = (char *) sd;
inode->i_op = &ext4_encrypted_symlink_inode_operations;
}
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
- if (!encryption_required)
+ if (!IS_ENCRYPTED(inode))
inode->i_op = &ext4_symlink_inode_operations;
inode_nohighmem(inode);
ext4_set_aops(inode);
@@ -3180,7 +3151,7 @@ static int ext4_symlink(struct inode *dir,
} else {
/* clear the extent format for fast symlink */
ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
- if (!encryption_required) {
+ if (!IS_ENCRYPTED(inode)) {
inode->i_op = &ext4_fast_symlink_inode_operations;
inode->i_link = (char *)&EXT4_I(inode)->i_data;
}
@@ -3195,16 +3166,17 @@ static int ext4_symlink(struct inode *dir,
if (handle)
ext4_journal_stop(handle);
- kfree(sd);
- return err;
+ goto out_free_encrypted_link;
+
err_drop_inode:
if (handle)
ext4_journal_stop(handle);
clear_nlink(inode);
unlock_new_inode(inode);
iput(inode);
-err_free_sd:
- kfree(sd);
+out_free_encrypted_link:
+ if (disk_link.name != (unsigned char *)symname)
+ kfree(disk_link.name);
return err;
}
@@ -3222,9 +3194,9 @@ static int ext4_link(struct dentry *old_dentry,
if (err)
return err;
- if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
- (!projid_eq(EXT4_I(dir)->i_projid,
- EXT4_I(old_dentry->d_inode)->i_projid)))
+ if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
+ (!projid_eq(EXT4_I(dir)->i_projid,
+ EXT4_I(old_dentry->d_inode)->i_projid)))
return -EXDEV;
err = dquot_initialize(dir);
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 50443bda8e98..b6bec270a8e4 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1477,7 +1477,7 @@ static int ext4_flex_group_add(struct super_block *sb,
goto exit_journal;
group = flex_gd->groups[0].group;
- BUG_ON(group != EXT4_SB(sb)->s_groups_count);
+ BUG_ON(group != sbi->s_groups_count);
err = ext4_add_new_descs(handle, sb, group,
resize_inode, flex_gd->count);
if (err)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5de959fb0244..39bf464c35f1 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/ext4/super.c
*
@@ -743,6 +744,7 @@ __acquires(bitlock)
}
ext4_unlock_group(sb, grp);
+ ext4_commit_super(sb, 1);
ext4_handle_error(sb);
/*
* We only get here in the ERRORS_RO case; relocking the group
@@ -871,7 +873,6 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_li_request(sb);
ext4_quota_off_umount(sb);
- flush_workqueue(sbi->rsv_conversion_wq);
destroy_workqueue(sbi->rsv_conversion_wq);
if (sbi->s_journal) {
@@ -1037,11 +1038,13 @@ static void init_once(void *foo)
static int __init init_inodecache(void)
{
- ext4_inode_cachep = kmem_cache_create("ext4_inode_cache",
- sizeof(struct ext4_inode_info),
- 0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_ACCOUNT),
- init_once);
+ ext4_inode_cachep = kmem_cache_create_usercopy("ext4_inode_cache",
+ sizeof(struct ext4_inode_info), 0,
+ (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT),
+ offsetof(struct ext4_inode_info, i_data),
+ sizeof_field(struct ext4_inode_info, i_data),
+ init_once);
if (ext4_inode_cachep == NULL)
return -ENOMEM;
return 0;
@@ -1070,9 +1073,7 @@ void ext4_clear_inode(struct inode *inode)
jbd2_free_inode(EXT4_I(inode)->jinode);
EXT4_I(inode)->jinode = NULL;
}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- fscrypt_put_encryption_info(inode, NULL);
-#endif
+ fscrypt_put_encryption_info(inode);
}
static struct inode *ext4_nfs_get_inode(struct super_block *sb,
@@ -2677,7 +2678,7 @@ static ext4_fsblk_t descriptor_loc(struct super_block *sb,
* compensate.
*/
if (sb->s_blocksize == 1024 && nr == 0 &&
- le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block) == 0)
+ le32_to_cpu(sbi->s_es->s_first_data_block) == 0)
has_super++;
return (has_super + ext4_group_first_block_no(sb, bg));
@@ -3122,7 +3123,7 @@ int ext4_register_li_request(struct super_block *sb,
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_li_request *elr = NULL;
- ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
+ ext4_group_t ngroups = sbi->s_groups_count;
int ret = 0;
mutex_lock(&ext4_li_mtx);
@@ -3711,11 +3712,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (ext4_has_feature_inline_data(sb)) {
ext4_msg(sb, KERN_ERR, "Cannot use DAX on a filesystem"
" that may contain inline data");
- goto failed_mount;
+ sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;
}
err = bdev_dax_supported(sb, blocksize);
- if (err)
- goto failed_mount;
+ if (err) {
+ ext4_msg(sb, KERN_ERR,
+ "DAX unsupported by block device. Turning off DAX.");
+ sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;
+ }
}
if (ext4_has_feature_encrypt(sb) && es->s_encryption_level) {
@@ -4837,7 +4841,7 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
bool needs_barrier = false;
struct ext4_sb_info *sbi = EXT4_SB(sb);
- if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
+ if (unlikely(ext4_forced_shutdown(sbi)))
return 0;
trace_ext4_sync_fs(sb, wait);
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index a2006c9af1d9..dd05af983092 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -28,59 +28,28 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry,
struct delayed_call *done)
{
struct page *cpage = NULL;
- char *caddr, *paddr = NULL;
- struct fscrypt_str cstr, pstr;
- struct fscrypt_symlink_data *sd;
- int res;
- u32 max_size = inode->i_sb->s_blocksize;
+ const void *caddr;
+ unsigned int max_size;
+ const char *paddr;
if (!dentry)
return ERR_PTR(-ECHILD);
- res = fscrypt_get_encryption_info(inode);
- if (res)
- return ERR_PTR(res);
-
if (ext4_inode_is_fast_symlink(inode)) {
- caddr = (char *) EXT4_I(inode)->i_data;
+ caddr = EXT4_I(inode)->i_data;
max_size = sizeof(EXT4_I(inode)->i_data);
} else {
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
if (IS_ERR(cpage))
return ERR_CAST(cpage);
caddr = page_address(cpage);
+ max_size = inode->i_sb->s_blocksize;
}
- /* Symlink is encrypted */
- sd = (struct fscrypt_symlink_data *)caddr;
- cstr.name = sd->encrypted_path;
- cstr.len = le16_to_cpu(sd->len);
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
- /* Symlink data on the disk is corrupted */
- res = -EFSCORRUPTED;
- goto errout;
- }
-
- res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
- if (res)
- goto errout;
- paddr = pstr.name;
-
- res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
- if (res)
- goto errout;
-
- /* Null-terminate the name */
- paddr[pstr.len] = '\0';
+ paddr = fscrypt_get_symlink(inode, caddr, max_size, done);
if (cpage)
put_page(cpage);
- set_delayed_call(done, kfree_link, paddr);
return paddr;
-errout:
- if (cpage)
- put_page(cpage);
- kfree(paddr);
- return ERR_PTR(res);
}
const struct inode_operations ext4_encrypted_symlink_inode_operations = {
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index e21afd52e7d7..1205261f130c 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -11,6 +11,7 @@
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/proc_fs.h>
#include "ext4.h"
@@ -329,6 +330,13 @@ static void ext4_sb_release(struct kobject *kobj)
complete(&sbi->s_kobj_unregister);
}
+static void ext4_kset_release(struct kobject *kobj)
+{
+ struct kset *kset = container_of(kobj, struct kset, kobj);
+
+ kfree(kset);
+}
+
static const struct sysfs_ops ext4_attr_ops = {
.show = ext4_attr_show,
.store = ext4_attr_store,
@@ -342,20 +350,18 @@ static struct kobj_type ext4_sb_ktype = {
static struct kobj_type ext4_ktype = {
.sysfs_ops = &ext4_attr_ops,
+ .release = ext4_kset_release,
};
-static struct kset ext4_kset = {
- .kobj = {.ktype = &ext4_ktype},
-};
+static struct kset *ext4_kset;
static struct kobj_type ext4_feat_ktype = {
.default_attrs = ext4_feat_attrs,
.sysfs_ops = &ext4_attr_ops,
+ .release = (void (*)(struct kobject *))kfree,
};
-static struct kobject ext4_feat = {
- .kset = &ext4_kset,
-};
+static struct kobject *ext4_feat;
#define PROC_FILE_SHOW_DEFN(name) \
static int name##_open(struct inode *inode, struct file *file) \
@@ -392,12 +398,15 @@ int ext4_register_sysfs(struct super_block *sb)
const struct ext4_proc_files *p;
int err;
- sbi->s_kobj.kset = &ext4_kset;
+ sbi->s_kobj.kset = ext4_kset;
init_completion(&sbi->s_kobj_unregister);
err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, NULL,
"%s", sb->s_id);
- if (err)
+ if (err) {
+ kobject_put(&sbi->s_kobj);
+ wait_for_completion(&sbi->s_kobj_unregister);
return err;
+ }
if (ext4_proc_root)
sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
@@ -427,25 +436,45 @@ int __init ext4_init_sysfs(void)
{
int ret;
- kobject_set_name(&ext4_kset.kobj, "ext4");
- ext4_kset.kobj.parent = fs_kobj;
- ret = kset_register(&ext4_kset);
+ ext4_kset = kzalloc(sizeof(*ext4_kset), GFP_KERNEL);
+ if (!ext4_kset)
+ return -ENOMEM;
+
+ kobject_set_name(&ext4_kset->kobj, "ext4");
+ ext4_kset->kobj.parent = fs_kobj;
+ ext4_kset->kobj.ktype = &ext4_ktype;
+ ret = kset_register(ext4_kset);
if (ret)
- return ret;
+ goto kset_err;
+
+ ext4_feat = kzalloc(sizeof(*ext4_feat), GFP_KERNEL);
+ if (!ext4_feat) {
+ ret = -ENOMEM;
+ goto kset_err;
+ }
- ret = kobject_init_and_add(&ext4_feat, &ext4_feat_ktype,
+ ext4_feat->kset = ext4_kset;
+ ret = kobject_init_and_add(ext4_feat, &ext4_feat_ktype,
NULL, "features");
if (ret)
- kset_unregister(&ext4_kset);
- else
- ext4_proc_root = proc_mkdir(proc_dirname, NULL);
+ goto feat_err;
+
+ ext4_proc_root = proc_mkdir(proc_dirname, NULL);
+ return ret;
+
+feat_err:
+ kobject_put(ext4_feat);
+kset_err:
+ kset_unregister(ext4_kset);
+ ext4_kset = NULL;
return ret;
}
void ext4_exit_sysfs(void)
{
- kobject_put(&ext4_feat);
- kset_unregister(&ext4_kset);
+ kobject_put(ext4_feat);
+ kset_unregister(ext4_kset);
+ ext4_kset = NULL;
remove_proc_entry(proc_dirname, NULL);
ext4_proc_root = NULL;
}
diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h
index b64a9fa0ff41..0cb13badf473 100644
--- a/fs/ext4/truncate.h
+++ b/fs/ext4/truncate.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/ext4/truncate.h
*
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index f8cc07588ac9..dd54c4f995c8 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
/*
File: fs/ext4/xattr.h
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 89c838bfb067..205add3d0f3a 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -585,7 +585,7 @@ no_delete:
!exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
}
out_clear:
- fscrypt_put_encryption_info(inode, NULL);
+ fscrypt_put_encryption_info(inode);
clear_inode(inode);
}
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index c4c94c7e9f4f..b68e7b03959f 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -486,27 +486,16 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode;
size_t len = strlen(symname);
- struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
- struct fscrypt_symlink_data *sd = NULL;
+ struct fscrypt_str disk_link;
int err;
if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
- if (f2fs_encrypted_inode(dir)) {
- err = fscrypt_get_encryption_info(dir);
- if (err)
- return err;
-
- if (!fscrypt_has_encryption_key(dir))
- return -ENOKEY;
-
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
- sizeof(struct fscrypt_symlink_data));
- }
-
- if (disk_link.len > dir->i_sb->s_blocksize)
- return -ENAMETOOLONG;
+ err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
+ &disk_link);
+ if (err)
+ return err;
err = dquot_initialize(dir);
if (err)
@@ -516,7 +505,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode))
return PTR_ERR(inode);
- if (f2fs_encrypted_inode(inode))
+ if (IS_ENCRYPTED(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
@@ -526,38 +515,13 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
if (err)
- goto out;
+ goto out_handle_failed_inode;
f2fs_unlock_op(sbi);
alloc_nid_done(sbi, inode->i_ino);
- if (f2fs_encrypted_inode(inode)) {
- struct qstr istr = QSTR_INIT(symname, len);
- struct fscrypt_str ostr;
-
- sd = f2fs_kzalloc(sbi, disk_link.len, GFP_NOFS);
- if (!sd) {
- err = -ENOMEM;
- goto err_out;
- }
-
- err = fscrypt_get_encryption_info(inode);
- if (err)
- goto err_out;
-
- if (!fscrypt_has_encryption_key(inode)) {
- err = -ENOKEY;
- goto err_out;
- }
-
- ostr.name = sd->encrypted_path;
- ostr.len = disk_link.len;
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
- if (err)
- goto err_out;
-
- sd->len = cpu_to_le16(ostr.len);
- disk_link.name = (char *)sd;
- }
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
+ if (err)
+ goto err_out;
err = page_symlink(inode, disk_link.name, disk_link.len);
@@ -584,12 +548,14 @@ err_out:
f2fs_unlink(dir, dentry);
}
- kfree(sd);
-
f2fs_balance_fs(sbi, true);
- return err;
-out:
+ goto out_free_encrypted_link;
+
+out_handle_failed_inode:
handle_failed_inode(inode);
+out_free_encrypted_link:
+ if (disk_link.name != (unsigned char *)symname)
+ kfree(disk_link.name);
return err;
}
@@ -1148,68 +1114,20 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
- struct page *cpage = NULL;
- char *caddr, *paddr = NULL;
- struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
- struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
- struct fscrypt_symlink_data *sd;
- u32 max_size = inode->i_sb->s_blocksize;
- int res;
+ struct page *page;
+ const char *target;
if (!dentry)
return ERR_PTR(-ECHILD);
- res = fscrypt_get_encryption_info(inode);
- if (res)
- return ERR_PTR(res);
-
- cpage = read_mapping_page(inode->i_mapping, 0, NULL);
- if (IS_ERR(cpage))
- return ERR_CAST(cpage);
- caddr = page_address(cpage);
-
- /* Symlink is encrypted */
- sd = (struct fscrypt_symlink_data *)caddr;
- cstr.name = sd->encrypted_path;
- cstr.len = le16_to_cpu(sd->len);
-
- /* this is broken symlink case */
- if (unlikely(cstr.len == 0)) {
- res = -ENOENT;
- goto errout;
- }
-
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
- /* Symlink data on the disk is corrupted */
- res = -EIO;
- goto errout;
- }
- res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
- if (res)
- goto errout;
-
- res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
- if (res)
- goto errout;
-
- /* this is broken symlink case */
- if (unlikely(pstr.name[0] == 0)) {
- res = -ENOENT;
- goto errout;
- }
-
- paddr = pstr.name;
-
- /* Null-terminate the name */
- paddr[pstr.len] = '\0';
+ page = read_mapping_page(inode->i_mapping, 0, NULL);
+ if (IS_ERR(page))
+ return ERR_CAST(page);
- put_page(cpage);
- set_delayed_call(done, kfree_link, paddr);
- return paddr;
-errout:
- fscrypt_fname_free_buffer(&pstr);
- put_page(cpage);
- return ERR_PTR(res);
+ target = fscrypt_get_symlink(inode, page_address(page),
+ inode->i_sb->s_blocksize, done);
+ put_page(page);
+ return target;
}
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
diff --git a/fs/fhandle.c b/fs/fhandle.c
index 0ace128f5d23..0ee727485615 100644
--- a/fs/fhandle.c
+++ b/fs/fhandle.c
@@ -69,8 +69,7 @@ static long do_sys_name_to_handle(struct path *path,
} else
retval = 0;
/* copy the mount id */
- if (copy_to_user(mnt_id, &real_mount(path->mnt)->mnt_id,
- sizeof(*mnt_id)) ||
+ if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) ||
copy_to_user(ufh, handle,
sizeof(struct file_handle) + handle_bytes))
retval = -EFAULT;
diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c
index f989efa051a0..48b24bb50d02 100644
--- a/fs/freevxfs/vxfs_super.c
+++ b/fs/freevxfs/vxfs_super.c
@@ -332,9 +332,13 @@ vxfs_init(void)
{
int rv;
- vxfs_inode_cachep = kmem_cache_create("vxfs_inode",
+ vxfs_inode_cachep = kmem_cache_create_usercopy("vxfs_inode",
sizeof(struct vxfs_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
+ offsetof(struct vxfs_inode_info, vii_immed.vi_immed),
+ sizeof_field(struct vxfs_inode_info,
+ vii_immed.vi_immed),
+ NULL);
if (!vxfs_inode_cachep)
return -ENOMEM;
rv = register_filesystem(&vxfs_fs_type);
diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig
index c0225d4b5435..3ed2b088dcfd 100644
--- a/fs/gfs2/Kconfig
+++ b/fs/gfs2/Kconfig
@@ -3,8 +3,7 @@ config GFS2_FS
depends on (64BIT || LBDAF)
select FS_POSIX_ACL
select CRC32
- select CRYPTO
- select CRYPTO_CRC32C
+ select LIBCRC32C
select QUOTACTL
select FS_IOMAP
help
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 90af87ff29ba..82fb5583445c 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1921,19 +1921,29 @@ void gfs2_glock_exit(void)
destroy_workqueue(gfs2_delete_workqueue);
}
-static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi)
+static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n)
{
- while ((gi->gl = rhashtable_walk_next(&gi->hti))) {
- if (IS_ERR(gi->gl)) {
- if (PTR_ERR(gi->gl) == -EAGAIN)
- continue;
- gi->gl = NULL;
- return;
+ if (n == 0)
+ gi->gl = rhashtable_walk_peek(&gi->hti);
+ else {
+ gi->gl = rhashtable_walk_next(&gi->hti);
+ n--;
+ }
+ for (;;) {
+ if (IS_ERR_OR_NULL(gi->gl)) {
+ if (!gi->gl)
+ return;
+ if (PTR_ERR(gi->gl) != -EAGAIN) {
+ gi->gl = NULL;
+ return;
+ }
+ n = 0;
+ } else if (gi->sdp == gi->gl->gl_name.ln_sbd &&
+ !__lockref_is_dead(&gi->gl->gl_lockref)) {
+ if (!n--)
+ break;
}
- /* Skip entries for other sb and dead entries */
- if (gi->sdp == gi->gl->gl_name.ln_sbd &&
- !__lockref_is_dead(&gi->gl->gl_lockref))
- return;
+ gi->gl = rhashtable_walk_next(&gi->hti);
}
}
@@ -1941,18 +1951,24 @@ static void *gfs2_glock_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(RCU)
{
struct gfs2_glock_iter *gi = seq->private;
- loff_t n = *pos;
+ loff_t n;
- rhashtable_walk_enter(&gl_hash_table, &gi->hti);
- if (rhashtable_walk_start_check(&gi->hti) != 0)
- return NULL;
+ /*
+ * We can either stay where we are, skip to the next hash table
+ * entry, or start from the beginning.
+ */
+ if (*pos < gi->last_pos) {
+ rhashtable_walk_exit(&gi->hti);
+ rhashtable_walk_enter(&gl_hash_table, &gi->hti);
+ n = *pos + 1;
+ } else {
+ n = *pos - gi->last_pos;
+ }
- do {
- gfs2_glock_iter_next(gi);
- } while (gi->gl && n--);
+ rhashtable_walk_start(&gi->hti);
+ gfs2_glock_iter_next(gi, n);
gi->last_pos = *pos;
-
return gi->gl;
}
@@ -1963,8 +1979,7 @@ static void *gfs2_glock_seq_next(struct seq_file *seq, void *iter_ptr,
(*pos)++;
gi->last_pos = *pos;
- gfs2_glock_iter_next(gi);
-
+ gfs2_glock_iter_next(gi, 1);
return gi->gl;
}
@@ -1975,7 +1990,6 @@ static void gfs2_glock_seq_stop(struct seq_file *seq, void *iter_ptr)
gi->gl = NULL;
rhashtable_walk_stop(&gi->hti);
- rhashtable_walk_exit(&gi->hti);
}
static int gfs2_glock_seq_show(struct seq_file *seq, void *iter_ptr)
@@ -2041,7 +2055,13 @@ static int __gfs2_glocks_open(struct inode *inode, struct file *file,
seq->buf = kmalloc(GFS2_SEQ_GOODSIZE, GFP_KERNEL | __GFP_NOWARN);
if (seq->buf)
seq->size = GFS2_SEQ_GOODSIZE;
+ /*
+ * Initially, we are "before" the first hash table entry; the
+ * first call to rhashtable_walk_next gets us the first entry.
+ */
+ gi->last_pos = -1;
gi->gl = NULL;
+ rhashtable_walk_enter(&gl_hash_table, &gi->hti);
}
return ret;
}
@@ -2057,6 +2077,7 @@ static int gfs2_glocks_release(struct inode *inode, struct file *file)
struct gfs2_glock_iter *gi = seq->private;
gi->gl = NULL;
+ rhashtable_walk_exit(&gi->hti);
return seq_release_private(inode, file);
}
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index e8120a282435..15e06fb552da 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -444,7 +444,7 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
int res = -ENOMEM;
mutex_lock(&sbi->vh_mutex);
- inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
+ inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
if (!inode)
goto out;
@@ -486,7 +486,7 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
int res = -ENOMEM;
mutex_lock(&sbi->vh_mutex);
- inode = hfsplus_new_inode(dir->i_sb, mode);
+ inode = hfsplus_new_inode(dir->i_sb, dir, mode);
if (!inode)
goto out;
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index a015044daa05..d9255abafb81 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -478,7 +478,8 @@ extern const struct address_space_operations hfsplus_aops;
extern const struct address_space_operations hfsplus_btree_aops;
extern const struct dentry_operations hfsplus_dentry_operations;
-struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode);
+struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir,
+ umode_t mode);
void hfsplus_delete_inode(struct inode *inode);
void hfsplus_inode_read_fork(struct inode *inode,
struct hfsplus_fork_raw *fork);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 190c60efbc99..c0c8d433864f 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -354,7 +354,8 @@ static const struct file_operations hfsplus_file_operations = {
.unlocked_ioctl = hfsplus_ioctl,
};
-struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
+struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir,
+ umode_t mode)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct inode *inode = new_inode(sb);
@@ -364,9 +365,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
return NULL;
inode->i_ino = sbi->next_cnid++;
- inode->i_mode = mode;
- inode->i_uid = current_fsuid();
- inode->i_gid = current_fsgid();
+ inode_init_owner(inode, dir, mode);
set_nlink(inode, 1);
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 1d458b716957..513c357c734b 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -549,7 +549,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
if (!sbi->hidden_dir) {
mutex_lock(&sbi->vh_mutex);
- sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
+ sbi->hidden_dir = hfsplus_new_inode(sb, root, S_IFDIR);
if (!sbi->hidden_dir) {
mutex_unlock(&sbi->vh_mutex);
err = -ENOMEM;
diff --git a/fs/inode.c b/fs/inode.c
index e2ca0f4b5151..ef362364d396 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -498,7 +498,6 @@ EXPORT_SYMBOL(__remove_inode_hash);
void clear_inode(struct inode *inode)
{
- might_sleep();
/*
* We have to cycle tree_lock here because reclaim can be still in the
* process of removing the last page (in __delete_from_page_cache())
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 4055f51617ef..c125d662777c 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/checkpoint.c
*
@@ -5,10 +6,6 @@
*
* Copyright 1999 Red Hat Software --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Checkpoint routines for the generic filesystem journaling code.
* Part of the ext2fs journaling system.
*
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 3c1c31321d9b..8de0e7723316 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/commit.c
*
@@ -5,10 +6,6 @@
*
* Copyright 1998 Red Hat corp --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Journal commit routines for the generic filesystem journaling code;
* part of the ext2fs journaling system.
*/
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 67546c7ad473..3fbf48ec2188 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/journal.c
*
@@ -5,10 +6,6 @@
*
* Copyright 1998 Red Hat corp --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Generic filesystem journal-writing code; part of the ext2fs
* journaling system.
*
diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c
index 02dd3360cb20..f99910b69c78 100644
--- a/fs/jbd2/recovery.c
+++ b/fs/jbd2/recovery.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/recovery.c
*
@@ -5,10 +6,6 @@
*
* Copyright 1999-2000 Red Hat Software --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Journal recovery routines for the generic filesystem journaling code;
* part of the ext2fs journaling system.
*/
diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c
index f9aefcda5854..696ef15ec942 100644
--- a/fs/jbd2/revoke.c
+++ b/fs/jbd2/revoke.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/revoke.c
*
@@ -5,10 +6,6 @@
*
* Copyright 2000 Red Hat corp --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Journal revoke routines for the generic filesystem journaling code;
* part of the ext2fs journaling system.
*
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 8b08044b3120..ac311037d7a5 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* linux/fs/jbd2/transaction.c
*
@@ -5,10 +6,6 @@
*
* Copyright 1998 Red Hat corp --- All Rights Reserved
*
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
* Generic filesystem transaction handling code; part of the ext2fs
* journaling system.
*
@@ -495,8 +492,10 @@ void jbd2_journal_free_reserved(handle_t *handle)
EXPORT_SYMBOL(jbd2_journal_free_reserved);
/**
- * int jbd2_journal_start_reserved(handle_t *handle) - start reserved handle
+ * int jbd2_journal_start_reserved() - start reserved handle
* @handle: handle to start
+ * @type: for handle statistics
+ * @line_no: for handle statistics
*
* Start handle that has been previously reserved with jbd2_journal_reserve().
* This attaches @handle to the running transaction (or creates one if there's
@@ -626,6 +625,7 @@ error_out:
* int jbd2_journal_restart() - restart a handle .
* @handle: handle to restart
* @nblocks: nr credits requested
+ * @gfp_mask: memory allocation flags (for start_this_handle)
*
* Restart a handle for a multi-transaction filesystem
* operation.
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 90373aebfdca..1b9264fd54b6 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -965,9 +965,11 @@ static int __init init_jfs_fs(void)
int rc;
jfs_inode_cachep =
- kmem_cache_create("jfs_ip", sizeof(struct jfs_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
- init_once);
+ kmem_cache_create_usercopy("jfs_ip", sizeof(struct jfs_inode_info),
+ 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
+ offsetof(struct jfs_inode_info, i_inline),
+ sizeof_field(struct jfs_inode_info, i_inline),
+ init_once);
if (jfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/mbcache.c b/fs/mbcache.c
index b8b8b9ced9f8..bf41e2e72c18 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -94,6 +94,7 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
entry->e_key = key;
entry->e_value = value;
entry->e_reusable = reusable;
+ entry->e_referenced = 0;
head = mb_cache_entry_head(cache, key);
hlist_bl_lock(head);
hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) {
@@ -238,7 +239,9 @@ void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value)
spin_lock(&cache->c_list_lock);
if (!list_empty(&entry->e_list)) {
list_del_init(&entry->e_list);
- cache->c_entry_count--;
+ if (!WARN_ONCE(cache->c_entry_count == 0,
+ "mbcache: attempt to decrement c_entry_count past zero"))
+ cache->c_entry_count--;
atomic_dec(&entry->e_refcnt);
}
spin_unlock(&cache->c_list_lock);
@@ -269,9 +272,6 @@ static unsigned long mb_cache_count(struct shrinker *shrink,
struct mb_cache *cache = container_of(shrink, struct mb_cache,
c_shrink);
- /* Unlikely, but not impossible */
- if (unlikely(cache->c_entry_count < 0))
- return 0;
return cache->c_entry_count;
}
diff --git a/fs/ncpfs/Kconfig b/fs/ncpfs/Kconfig
deleted file mode 100644
index c931cf22a1f6..000000000000
--- a/fs/ncpfs/Kconfig
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# NCP Filesystem configuration
-#
-config NCP_FS
- tristate "NCP file system support (to mount NetWare volumes)"
- depends on IPX!=n || INET
- help
- NCP (NetWare Core Protocol) is a protocol that runs over IPX and is
- used by Novell NetWare clients to talk to file servers. It is to
- IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you
- to mount NetWare file server volumes and to access them just like
- any other Unix directory. For details, please read the file
- <file:Documentation/filesystems/ncpfs.txt> in the kernel source and
- the IPX-HOWTO from <http://www.tldp.org/docs.html#howto>.
-
- You do not have to say Y here if you want your Linux box to act as a
- file *server* for Novell NetWare clients.
-
- General information about how to connect Linux, Windows machines and
- Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>.
-
- To compile this as a module, choose M here: the module will be called
- ncpfs. Say N unless you are connected to a Novell network.
-
-config NCPFS_PACKET_SIGNING
- bool "Packet signatures"
- depends on NCP_FS
- help
- NCP allows packets to be signed for stronger security. If you want
- security, say Y. Normal users can leave it off. To be able to use
- packet signing you must use ncpfs > 2.0.12.
-
-config NCPFS_IOCTL_LOCKING
- bool "Proprietary file locking"
- depends on NCP_FS
- help
- Allows locking of records on remote volumes. Say N unless you have
- special applications which are able to utilize this locking scheme.
-
-config NCPFS_STRONG
- bool "Clear remove/delete inhibit when needed"
- depends on NCP_FS
- help
- Allows manipulation of files flagged as Delete or Rename Inhibit.
- To use this feature you must mount volumes with the ncpmount
- parameter "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not
- mounting volumes with -f 444.
-
-config NCPFS_NFS_NS
- bool "Use NFS namespace if available"
- depends on NCP_FS
- help
- Allows you to utilize NFS namespace on NetWare servers. It brings
- you case sensitive filenames. Say Y. You can disable it at
- mount-time with the `-N nfs' parameter of ncpmount.
-
-config NCPFS_OS2_NS
- bool "Use LONG (OS/2) namespace if available"
- depends on NCP_FS
- help
- Allows you to utilize OS2/LONG namespace on NetWare servers.
- Filenames in this namespace are limited to 255 characters, they are
- case insensitive, and case in names is preserved. Say Y. You can
- disable it at mount time with the -N os2 parameter of ncpmount.
-
-config NCPFS_SMALLDOS
- bool "Lowercase DOS filenames"
- depends on NCP_FS
- ---help---
- If you say Y here, every filename on a NetWare server volume using
- the OS2/LONG namespace and created under DOS or on a volume using
- DOS namespace will be converted to lowercase characters.
- Saying N here will give you these filenames in uppercase.
-
- This is only a cosmetic option since the OS2/LONG namespace is case
- insensitive. The only major reason for this option is backward
- compatibility when moving from DOS to OS2/LONG namespace support.
- Long filenames (created by Win95) will not be affected.
-
- This option does not solve the problem that filenames appear
- differently under Linux and under Windows, since Windows does an
- additional conversions on the client side. You can achieve similar
- effects by saying Y to "Allow using of Native Language Support"
- below.
-
-config NCPFS_NLS
- bool "Use Native Language Support"
- depends on NCP_FS
- select NLS
- help
- Allows you to use codepages and I/O charsets for file name
- translation between the server file system and input/output. This
- may be useful, if you want to access the server with other operating
- systems, e.g. Windows 95. See also NLS for more Information.
-
- To select codepages and I/O charsets use ncpfs-2.2.0.13 or newer.
-
-config NCPFS_EXTRAS
- bool "Enable symbolic links and execute flags"
- depends on NCP_FS
- help
- This enables the use of symbolic links and an execute permission
- bit on NCPFS. The file server need not have long name space or NFS
- name space loaded for these to work.
-
- To use the new attributes, it is recommended to use the flags
- '-f 600 -d 755' on the ncpmount command line.
-
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
deleted file mode 100644
index 66fe5f878817..000000000000
--- a/fs/ncpfs/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for the linux ncp filesystem routines.
-#
-
-obj-$(CONFIG_NCP_FS) += ncpfs.o
-
-ncpfs-y := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
- ncpsign_kernel.o getopt.o
-
-ncpfs-$(CONFIG_NCPFS_EXTRAS) += symlink.o
-ncpfs-$(CONFIG_NCPFS_NFS_NS) += symlink.o
-
-# If you want debugging output, please uncomment the following line
-# ccflags-y := -DDEBUG_NCP=1
-
-CFLAGS_ncplib_kernel.o := -finline-functions
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
deleted file mode 100644
index 0c57c5c5d40a..000000000000
--- a/fs/ncpfs/dir.c
+++ /dev/null
@@ -1,1232 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * dir.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified for big endian by J.F. Chadima and David S. Miller
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- * Modified 1998, 1999 Wolfram Pienkoss for NLS
- * Modified 1999 Wolfram Pienkoss for directory caching
- * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
- *
- */
-
-
-#include <linux/time.h>
-#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/namei.h>
-#include <linux/uaccess.h>
-#include <asm/byteorder.h>
-
-#include "ncp_fs.h"
-
-static void ncp_read_volume_list(struct file *, struct dir_context *,
- struct ncp_cache_control *);
-static void ncp_do_readdir(struct file *, struct dir_context *,
- struct ncp_cache_control *);
-
-static int ncp_readdir(struct file *, struct dir_context *);
-
-static int ncp_create(struct inode *, struct dentry *, umode_t, bool);
-static struct dentry *ncp_lookup(struct inode *, struct dentry *, unsigned int);
-static int ncp_unlink(struct inode *, struct dentry *);
-static int ncp_mkdir(struct inode *, struct dentry *, umode_t);
-static int ncp_rmdir(struct inode *, struct dentry *);
-static int ncp_rename(struct inode *, struct dentry *,
- struct inode *, struct dentry *, unsigned int);
-static int ncp_mknod(struct inode * dir, struct dentry *dentry,
- umode_t mode, dev_t rdev);
-#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
-extern int ncp_symlink(struct inode *, struct dentry *, const char *);
-#else
-#define ncp_symlink NULL
-#endif
-
-const struct file_operations ncp_dir_operations =
-{
- .llseek = generic_file_llseek,
- .read = generic_read_dir,
- .iterate = ncp_readdir,
- .unlocked_ioctl = ncp_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ncp_compat_ioctl,
-#endif
-};
-
-const struct inode_operations ncp_dir_inode_operations =
-{
- .create = ncp_create,
- .lookup = ncp_lookup,
- .unlink = ncp_unlink,
- .symlink = ncp_symlink,
- .mkdir = ncp_mkdir,
- .rmdir = ncp_rmdir,
- .mknod = ncp_mknod,
- .rename = ncp_rename,
- .setattr = ncp_notify_change,
-};
-
-/*
- * Dentry operations routines
- */
-static int ncp_lookup_validate(struct dentry *, unsigned int);
-static int ncp_hash_dentry(const struct dentry *, struct qstr *);
-static int ncp_compare_dentry(const struct dentry *,
- unsigned int, const char *, const struct qstr *);
-static int ncp_delete_dentry(const struct dentry *);
-static void ncp_d_prune(struct dentry *dentry);
-
-const struct dentry_operations ncp_dentry_operations =
-{
- .d_revalidate = ncp_lookup_validate,
- .d_hash = ncp_hash_dentry,
- .d_compare = ncp_compare_dentry,
- .d_delete = ncp_delete_dentry,
- .d_prune = ncp_d_prune,
-};
-
-#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
-
-static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
-{
-#ifdef CONFIG_NCPFS_SMALLDOS
- int ns = ncp_namespace(i);
-
- if ((ns == NW_NS_DOS)
-#ifdef CONFIG_NCPFS_OS2_NS
- || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
-#endif /* CONFIG_NCPFS_OS2_NS */
- )
- return 0;
-#endif /* CONFIG_NCPFS_SMALLDOS */
- return 1;
-}
-
-#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS)
-
-static inline int ncp_case_sensitive(const struct inode *i)
-{
-#ifdef CONFIG_NCPFS_NFS_NS
- return ncp_namespace(i) == NW_NS_NFS;
-#else
- return 0;
-#endif /* CONFIG_NCPFS_NFS_NS */
-}
-
-/*
- * Note: leave the hash unchanged if the directory
- * is case-sensitive.
- */
-static int
-ncp_hash_dentry(const struct dentry *dentry, struct qstr *this)
-{
- struct inode *inode = d_inode_rcu(dentry);
-
- if (!inode)
- return 0;
-
- if (!ncp_case_sensitive(inode)) {
- struct nls_table *t;
- unsigned long hash;
- int i;
-
- t = NCP_IO_TABLE(dentry->d_sb);
- hash = init_name_hash(dentry);
- for (i=0; i<this->len ; i++)
- hash = partial_name_hash(ncp_tolower(t, this->name[i]),
- hash);
- this->hash = end_name_hash(hash);
- }
- return 0;
-}
-
-static int
-ncp_compare_dentry(const struct dentry *dentry,
- unsigned int len, const char *str, const struct qstr *name)
-{
- struct inode *pinode;
-
- if (len != name->len)
- return 1;
-
- pinode = d_inode_rcu(dentry->d_parent);
- if (!pinode)
- return 1;
-
- if (ncp_case_sensitive(pinode))
- return strncmp(str, name->name, len);
-
- return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
-}
-
-/*
- * This is the callback from dput() when d_count is going to 0.
- * We use this to unhash dentries with bad inodes.
- * Closing files can be safely postponed until iput() - it's done there anyway.
- */
-static int
-ncp_delete_dentry(const struct dentry * dentry)
-{
- struct inode *inode = d_inode(dentry);
-
- if (inode) {
- if (is_bad_inode(inode))
- return 1;
- } else
- {
- /* N.B. Unhash negative dentries? */
- }
- return 0;
-}
-
-static inline int
-ncp_single_volume(struct ncp_server *server)
-{
- return (server->m.mounted_vol[0] != '\0');
-}
-
-static inline int ncp_is_server_root(struct inode *inode)
-{
- return !ncp_single_volume(NCP_SERVER(inode)) &&
- is_root_inode(inode);
-}
-
-
-/*
- * This is the callback when the dcache has a lookup hit.
- */
-
-
-#ifdef CONFIG_NCPFS_STRONG
-/* try to delete a readonly file (NW R bit set) */
-
-static int
-ncp_force_unlink(struct inode *dir, struct dentry* dentry)
-{
- int res=0x9c,res2;
- struct nw_modify_dos_info info;
- __le32 old_nwattr;
- struct inode *inode;
-
- memset(&info, 0, sizeof(info));
-
- /* remove the Read-Only flag on the NW server */
- inode = d_inode(dentry);
-
- old_nwattr = NCP_FINFO(inode)->nwattr;
- info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
- if (res2)
- goto leave_me;
-
- /* now try again the delete operation */
- res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
-
- if (res) /* delete failed, set R bit again */
- {
- info.attributes = old_nwattr;
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
- if (res2)
- goto leave_me;
- }
-leave_me:
- return(res);
-}
-#endif /* CONFIG_NCPFS_STRONG */
-
-#ifdef CONFIG_NCPFS_STRONG
-static int
-ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
- struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
-{
- struct nw_modify_dos_info info;
- int res=0x90,res2;
- struct inode *old_inode = d_inode(old_dentry);
- __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
- __le32 new_nwattr = 0; /* shut compiler warning */
- int old_nwattr_changed = 0;
- int new_nwattr_changed = 0;
-
- memset(&info, 0, sizeof(info));
-
- /* remove the Read-Only flag on the NW server */
-
- info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
- if (!res2)
- old_nwattr_changed = 1;
- if (new_dentry && d_really_is_positive(new_dentry)) {
- new_nwattr = NCP_FINFO(d_inode(new_dentry))->nwattr;
- info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
- if (!res2)
- new_nwattr_changed = 1;
- }
- /* now try again the rename operation */
- /* but only if something really happened */
- if (new_nwattr_changed || old_nwattr_changed) {
- res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
- old_dir, _old_name,
- new_dir, _new_name);
- }
- if (res)
- goto leave_me;
- /* file was successfully renamed, so:
- do not set attributes on old file - it no longer exists
- copy attributes from old file to new */
- new_nwattr_changed = old_nwattr_changed;
- new_nwattr = old_nwattr;
- old_nwattr_changed = 0;
-
-leave_me:;
- if (old_nwattr_changed) {
- info.attributes = old_nwattr;
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
- /* ignore errors */
- }
- if (new_nwattr_changed) {
- info.attributes = new_nwattr;
- res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
- /* ignore errors */
- }
- return(res);
-}
-#endif /* CONFIG_NCPFS_STRONG */
-
-
-static int
-ncp_lookup_validate(struct dentry *dentry, unsigned int flags)
-{
- struct ncp_server *server;
- struct dentry *parent;
- struct inode *dir;
- struct ncp_entry_info finfo;
- int res, val = 0, len;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- if (dentry == dentry->d_sb->s_root)
- return 1;
-
- if (flags & LOOKUP_RCU)
- return -ECHILD;
-
- parent = dget_parent(dentry);
- dir = d_inode(parent);
-
- if (d_really_is_negative(dentry))
- goto finished;
-
- server = NCP_SERVER(dir);
-
- /*
- * Inspired by smbfs:
- * The default validation is based on dentry age:
- * We set the max age at mount time. (But each
- * successful server lookup renews the timestamp.)
- */
- val = NCP_TEST_AGE(server, dentry);
- if (val)
- goto finished;
-
- ncp_dbg(2, "%pd2 not valid, age=%ld, server lookup\n",
- dentry, NCP_GET_AGE(dentry));
-
- len = sizeof(__name);
- if (ncp_is_server_root(dir)) {
- res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, 1);
- if (!res) {
- res = ncp_lookup_volume(server, __name, &(finfo.i));
- if (!res)
- ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
- }
- } else {
- res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, !ncp_preserve_case(dir));
- if (!res)
- res = ncp_obtain_info(server, dir, __name, &(finfo.i));
- }
- finfo.volume = finfo.i.volNumber;
- ncp_dbg(2, "looked for %pd/%s, res=%d\n",
- dentry->d_parent, __name, res);
- /*
- * If we didn't find it, or if it has a different dirEntNum to
- * what we remember, it's not valid any more.
- */
- if (!res) {
- struct inode *inode = d_inode(dentry);
-
- inode_lock(inode);
- if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) {
- ncp_new_dentry(dentry);
- val=1;
- } else
- ncp_dbg(2, "found, but dirEntNum changed\n");
-
- ncp_update_inode2(inode, &finfo);
- inode_unlock(inode);
- }
-
-finished:
- ncp_dbg(2, "result=%d\n", val);
- dput(parent);
- return val;
-}
-
-static time_t ncp_obtain_mtime(struct dentry *dentry)
-{
- struct inode *inode = d_inode(dentry);
- struct ncp_server *server = NCP_SERVER(inode);
- struct nw_info_struct i;
-
- if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
- return 0;
-
- if (ncp_obtain_info(server, inode, NULL, &i))
- return 0;
-
- return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
-}
-
-static inline void
-ncp_invalidate_dircache_entries(struct dentry *parent)
-{
- struct ncp_server *server = NCP_SERVER(d_inode(parent));
- struct dentry *dentry;
-
- spin_lock(&parent->d_lock);
- list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- dentry->d_fsdata = NULL;
- ncp_age_dentry(server, dentry);
- }
- spin_unlock(&parent->d_lock);
-}
-
-static int ncp_readdir(struct file *file, struct dir_context *ctx)
-{
- struct dentry *dentry = file->f_path.dentry;
- struct inode *inode = d_inode(dentry);
- struct page *page = NULL;
- struct ncp_server *server = NCP_SERVER(inode);
- union ncp_dir_cache *cache = NULL;
- struct ncp_cache_control ctl;
- int result, mtime_valid = 0;
- time_t mtime = 0;
-
- ctl.page = NULL;
- ctl.cache = NULL;
-
- ncp_dbg(2, "reading %pD2, pos=%d\n", file, (int)ctx->pos);
-
- result = -EIO;
- /* Do not generate '.' and '..' when server is dead. */
- if (!ncp_conn_valid(server))
- goto out;
-
- result = 0;
- if (!dir_emit_dots(file, ctx))
- goto out;
-
- page = grab_cache_page(&inode->i_data, 0);
- if (!page)
- goto read_really;
-
- ctl.cache = cache = kmap(page);
- ctl.head = cache->head;
-
- if (!PageUptodate(page) || !ctl.head.eof)
- goto init_cache;
-
- if (ctx->pos == 2) {
- if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
- goto init_cache;
-
- mtime = ncp_obtain_mtime(dentry);
- mtime_valid = 1;
- if ((!mtime) || (mtime != ctl.head.mtime))
- goto init_cache;
- }
-
- if (ctx->pos > ctl.head.end)
- goto finished;
-
- ctl.fpos = ctx->pos + (NCP_DIRCACHE_START - 2);
- ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
- ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
-
- for (;;) {
- if (ctl.ofs != 0) {
- ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
- if (!ctl.page)
- goto invalid_cache;
- ctl.cache = kmap(ctl.page);
- if (!PageUptodate(ctl.page))
- goto invalid_cache;
- }
- while (ctl.idx < NCP_DIRCACHE_SIZE) {
- struct dentry *dent;
- bool over;
-
- spin_lock(&dentry->d_lock);
- if (!(NCP_FINFO(inode)->flags & NCPI_DIR_CACHE)) {
- spin_unlock(&dentry->d_lock);
- goto invalid_cache;
- }
- dent = ctl.cache->dentry[ctl.idx];
- if (unlikely(!lockref_get_not_dead(&dent->d_lockref))) {
- spin_unlock(&dentry->d_lock);
- goto invalid_cache;
- }
- spin_unlock(&dentry->d_lock);
- if (d_really_is_negative(dent)) {
- dput(dent);
- goto invalid_cache;
- }
- over = !dir_emit(ctx, dent->d_name.name,
- dent->d_name.len,
- d_inode(dent)->i_ino, DT_UNKNOWN);
- dput(dent);
- if (over)
- goto finished;
- ctx->pos += 1;
- ctl.idx += 1;
- if (ctx->pos > ctl.head.end)
- goto finished;
- }
- if (ctl.page) {
- kunmap(ctl.page);
- SetPageUptodate(ctl.page);
- unlock_page(ctl.page);
- put_page(ctl.page);
- ctl.page = NULL;
- }
- ctl.idx = 0;
- ctl.ofs += 1;
- }
-invalid_cache:
- if (ctl.page) {
- kunmap(ctl.page);
- unlock_page(ctl.page);
- put_page(ctl.page);
- ctl.page = NULL;
- }
- ctl.cache = cache;
-init_cache:
- ncp_invalidate_dircache_entries(dentry);
- if (!mtime_valid) {
- mtime = ncp_obtain_mtime(dentry);
- mtime_valid = 1;
- }
- ctl.head.mtime = mtime;
- ctl.head.time = jiffies;
- ctl.head.eof = 0;
- ctl.fpos = 2;
- ctl.ofs = 0;
- ctl.idx = NCP_DIRCACHE_START;
- ctl.filled = 0;
- ctl.valid = 1;
-read_really:
- spin_lock(&dentry->d_lock);
- NCP_FINFO(inode)->flags |= NCPI_DIR_CACHE;
- spin_unlock(&dentry->d_lock);
- if (ncp_is_server_root(inode)) {
- ncp_read_volume_list(file, ctx, &ctl);
- } else {
- ncp_do_readdir(file, ctx, &ctl);
- }
- ctl.head.end = ctl.fpos - 1;
- ctl.head.eof = ctl.valid;
-finished:
- if (ctl.page) {
- kunmap(ctl.page);
- SetPageUptodate(ctl.page);
- unlock_page(ctl.page);
- put_page(ctl.page);
- }
- if (page) {
- cache->head = ctl.head;
- kunmap(page);
- SetPageUptodate(page);
- unlock_page(page);
- put_page(page);
- }
-out:
- return result;
-}
-
-static void ncp_d_prune(struct dentry *dentry)
-{
- if (!dentry->d_fsdata) /* not referenced from page cache */
- return;
- NCP_FINFO(d_inode(dentry->d_parent))->flags &= ~NCPI_DIR_CACHE;
-}
-
-static int
-ncp_fill_cache(struct file *file, struct dir_context *ctx,
- struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
- int inval_childs)
-{
- struct dentry *newdent, *dentry = file->f_path.dentry;
- struct inode *dir = d_inode(dentry);
- struct ncp_cache_control ctl = *ctrl;
- struct qstr qname;
- int valid = 0;
- int hashed = 0;
- ino_t ino = 0;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- qname.len = sizeof(__name);
- if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len,
- entry->i.entryName, entry->i.nameLen,
- !ncp_preserve_entry_case(dir, entry->i.NSCreator)))
- return 1; /* I'm not sure */
-
- qname.name = __name;
-
- newdent = d_hash_and_lookup(dentry, &qname);
- if (IS_ERR(newdent))
- goto end_advance;
- if (!newdent) {
- newdent = d_alloc(dentry, &qname);
- if (!newdent)
- goto end_advance;
- } else {
- hashed = 1;
-
- /* If case sensitivity changed for this volume, all entries below this one
- should be thrown away. This entry itself is not affected, as its case
- sensitivity is controlled by its own parent. */
- if (inval_childs)
- shrink_dcache_parent(newdent);
-
- /*
- * NetWare's OS2 namespace is case preserving yet case
- * insensitive. So we update dentry's name as received from
- * server. Parent dir's i_mutex is locked because we're in
- * readdir.
- */
- dentry_update_name_case(newdent, &qname);
- }
-
- if (d_really_is_negative(newdent)) {
- struct inode *inode;
-
- entry->opened = 0;
- entry->ino = iunique(dir->i_sb, 2);
- inode = ncp_iget(dir->i_sb, entry);
- if (inode) {
- d_instantiate(newdent, inode);
- if (!hashed)
- d_rehash(newdent);
- } else {
- spin_lock(&dentry->d_lock);
- NCP_FINFO(dir)->flags &= ~NCPI_DIR_CACHE;
- spin_unlock(&dentry->d_lock);
- }
- } else {
- struct inode *inode = d_inode(newdent);
-
- inode_lock_nested(inode, I_MUTEX_CHILD);
- ncp_update_inode2(inode, entry);
- inode_unlock(inode);
- }
-
- if (ctl.idx >= NCP_DIRCACHE_SIZE) {
- if (ctl.page) {
- kunmap(ctl.page);
- SetPageUptodate(ctl.page);
- unlock_page(ctl.page);
- put_page(ctl.page);
- }
- ctl.cache = NULL;
- ctl.idx -= NCP_DIRCACHE_SIZE;
- ctl.ofs += 1;
- ctl.page = grab_cache_page(&dir->i_data, ctl.ofs);
- if (ctl.page)
- ctl.cache = kmap(ctl.page);
- }
- if (ctl.cache) {
- if (d_really_is_positive(newdent)) {
- newdent->d_fsdata = newdent;
- ctl.cache->dentry[ctl.idx] = newdent;
- ino = d_inode(newdent)->i_ino;
- ncp_new_dentry(newdent);
- }
- valid = 1;
- }
- dput(newdent);
-end_advance:
- if (!valid)
- ctl.valid = 0;
- if (!ctl.filled && (ctl.fpos == ctx->pos)) {
- if (!ino)
- ino = iunique(dir->i_sb, 2);
- ctl.filled = !dir_emit(ctx, qname.name, qname.len,
- ino, DT_UNKNOWN);
- if (!ctl.filled)
- ctx->pos += 1;
- }
- ctl.fpos += 1;
- ctl.idx += 1;
- *ctrl = ctl;
- return (ctl.valid || !ctl.filled);
-}
-
-static void
-ncp_read_volume_list(struct file *file, struct dir_context *ctx,
- struct ncp_cache_control *ctl)
-{
- struct inode *inode = file_inode(file);
- struct ncp_server *server = NCP_SERVER(inode);
- struct ncp_volume_info info;
- struct ncp_entry_info entry;
- int i;
-
- ncp_dbg(1, "pos=%ld\n", (unsigned long)ctx->pos);
-
- for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
- int inval_dentry;
-
- if (ncp_get_volume_info_with_number(server, i, &info) != 0)
- return;
- if (!strlen(info.volume_name))
- continue;
-
- ncp_dbg(1, "found vol: %s\n", info.volume_name);
-
- if (ncp_lookup_volume(server, info.volume_name,
- &entry.i)) {
- ncp_dbg(1, "could not lookup vol %s\n",
- info.volume_name);
- continue;
- }
- inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
- entry.volume = entry.i.volNumber;
- if (!ncp_fill_cache(file, ctx, ctl, &entry, inval_dentry))
- return;
- }
-}
-
-static void
-ncp_do_readdir(struct file *file, struct dir_context *ctx,
- struct ncp_cache_control *ctl)
-{
- struct inode *dir = file_inode(file);
- struct ncp_server *server = NCP_SERVER(dir);
- struct nw_search_sequence seq;
- struct ncp_entry_info entry;
- int err;
- void* buf;
- int more;
- size_t bufsize;
-
- ncp_dbg(1, "%pD2, fpos=%ld\n", file, (unsigned long)ctx->pos);
- ncp_vdbg("init %pD, volnum=%d, dirent=%u\n",
- file, NCP_FINFO(dir)->volNumber, NCP_FINFO(dir)->dirEntNum);
-
- err = ncp_initialize_search(server, dir, &seq);
- if (err) {
- ncp_dbg(1, "init failed, err=%d\n", err);
- return;
- }
- /* We MUST NOT use server->buffer_size handshaked with server if we are
- using UDP, as for UDP server uses max. buffer size determined by
- MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
- So we use 128KB, just to be sure, as there is no way how to know
- this value in advance. */
- bufsize = 131072;
- buf = vmalloc(bufsize);
- if (!buf)
- return;
- do {
- int cnt;
- char* rpl;
- size_t rpls;
-
- err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls);
- if (err) /* Error */
- break;
- if (!cnt) /* prevent endless loop */
- break;
- while (cnt--) {
- size_t onerpl;
-
- if (rpls < offsetof(struct nw_info_struct, entryName))
- break; /* short packet */
- ncp_extract_file_info(rpl, &entry.i);
- onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen;
- if (rpls < onerpl)
- break; /* short packet */
- (void)ncp_obtain_nfs_info(server, &entry.i);
- rpl += onerpl;
- rpls -= onerpl;
- entry.volume = entry.i.volNumber;
- if (!ncp_fill_cache(file, ctx, ctl, &entry, 0))
- break;
- }
- } while (more);
- vfree(buf);
- return;
-}
-
-int ncp_conn_logged_in(struct super_block *sb)
-{
- struct ncp_server* server = NCP_SBP(sb);
- int result;
-
- if (ncp_single_volume(server)) {
- int len;
- struct dentry* dent;
- __u32 volNumber;
- __le32 dirEntNum;
- __le32 DosDirNum;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- len = sizeof(__name);
- result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
- strlen(server->m.mounted_vol), 1);
- if (result)
- goto out;
- result = -ENOENT;
- if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) {
- ncp_vdbg("%s not found\n", server->m.mounted_vol);
- goto out;
- }
- dent = sb->s_root;
- if (dent) {
- struct inode* ino = d_inode(dent);
- if (ino) {
- ncp_update_known_namespace(server, volNumber, NULL);
- NCP_FINFO(ino)->volNumber = volNumber;
- NCP_FINFO(ino)->dirEntNum = dirEntNum;
- NCP_FINFO(ino)->DosDirNum = DosDirNum;
- result = 0;
- } else {
- ncp_dbg(1, "d_inode(sb->s_root) == NULL!\n");
- }
- } else {
- ncp_dbg(1, "sb->s_root == NULL!\n");
- }
- } else
- result = 0;
-
-out:
- return result;
-}
-
-static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
-{
- struct ncp_server *server = NCP_SERVER(dir);
- struct inode *inode = NULL;
- struct ncp_entry_info finfo;
- int error, res, len;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- error = -EIO;
- if (!ncp_conn_valid(server))
- goto finished;
-
- ncp_vdbg("server lookup for %pd2\n", dentry);
-
- len = sizeof(__name);
- if (ncp_is_server_root(dir)) {
- res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, 1);
- if (!res)
- res = ncp_lookup_volume(server, __name, &(finfo.i));
- if (!res)
- ncp_update_known_namespace(server, finfo.i.volNumber, NULL);
- } else {
- res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, !ncp_preserve_case(dir));
- if (!res)
- res = ncp_obtain_info(server, dir, __name, &(finfo.i));
- }
- ncp_vdbg("looked for %pd2, res=%d\n", dentry, res);
- /*
- * If we didn't find an entry, make a negative dentry.
- */
- if (res)
- goto add_entry;
-
- /*
- * Create an inode for the entry.
- */
- finfo.opened = 0;
- finfo.ino = iunique(dir->i_sb, 2);
- finfo.volume = finfo.i.volNumber;
- error = -EACCES;
- inode = ncp_iget(dir->i_sb, &finfo);
-
- if (inode) {
- ncp_new_dentry(dentry);
-add_entry:
- d_add(dentry, inode);
- error = 0;
- }
-
-finished:
- ncp_vdbg("result=%d\n", error);
- return ERR_PTR(error);
-}
-
-/*
- * This code is common to create, mkdir, and mknod.
- */
-static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
- struct ncp_entry_info *finfo)
-{
- struct inode *inode;
- int error = -EINVAL;
-
- finfo->ino = iunique(dir->i_sb, 2);
- inode = ncp_iget(dir->i_sb, finfo);
- if (!inode)
- goto out_close;
- d_instantiate(dentry,inode);
- error = 0;
-out:
- return error;
-
-out_close:
- ncp_vdbg("%pd2 failed, closing file\n", dentry);
- ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
- goto out;
-}
-
-int ncp_create_new(struct inode *dir, struct dentry *dentry, umode_t mode,
- dev_t rdev, __le32 attributes)
-{
- struct ncp_server *server = NCP_SERVER(dir);
- struct ncp_entry_info finfo;
- int error, result, len;
- int opmode;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- ncp_vdbg("creating %pd2, mode=%hx\n", dentry, mode);
-
- ncp_age_dentry(server, dentry);
- len = sizeof(__name);
- error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, !ncp_preserve_case(dir));
- if (error)
- goto out;
-
- error = -EACCES;
-
- if (S_ISREG(mode) &&
- (server->m.flags & NCP_MOUNT_EXTRAS) &&
- (mode & S_IXUGO))
- attributes |= aSYSTEM | aSHARED;
-
- result = ncp_open_create_file_or_subdir(server, dir, __name,
- OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
- attributes, AR_READ | AR_WRITE, &finfo);
- opmode = O_RDWR;
- if (result) {
- result = ncp_open_create_file_or_subdir(server, dir, __name,
- OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
- attributes, AR_WRITE, &finfo);
- if (result) {
- if (result == 0x87)
- error = -ENAMETOOLONG;
- else if (result < 0)
- error = result;
- ncp_dbg(1, "%pd2 failed\n", dentry);
- goto out;
- }
- opmode = O_WRONLY;
- }
- finfo.access = opmode;
- if (ncp_is_nfs_extras(server, finfo.volume)) {
- finfo.i.nfs.mode = mode;
- finfo.i.nfs.rdev = new_encode_dev(rdev);
- if (ncp_modify_nfs_info(server, finfo.volume,
- finfo.i.dirEntNum,
- mode, new_encode_dev(rdev)) != 0)
- goto out;
- }
-
- error = ncp_instantiate(dir, dentry, &finfo);
-out:
- return error;
-}
-
-static int ncp_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool excl)
-{
- return ncp_create_new(dir, dentry, mode, 0, 0);
-}
-
-static int ncp_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
-{
- struct ncp_entry_info finfo;
- struct ncp_server *server = NCP_SERVER(dir);
- int error, len;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- ncp_dbg(1, "making %pd2\n", dentry);
-
- ncp_age_dentry(server, dentry);
- len = sizeof(__name);
- error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, !ncp_preserve_case(dir));
- if (error)
- goto out;
-
- error = ncp_open_create_file_or_subdir(server, dir, __name,
- OC_MODE_CREATE, aDIR,
- cpu_to_le16(0xffff),
- &finfo);
- if (error == 0) {
- if (ncp_is_nfs_extras(server, finfo.volume)) {
- mode |= S_IFDIR;
- finfo.i.nfs.mode = mode;
- if (ncp_modify_nfs_info(server,
- finfo.volume,
- finfo.i.dirEntNum,
- mode, 0) != 0)
- goto out;
- }
- error = ncp_instantiate(dir, dentry, &finfo);
- } else if (error > 0) {
- error = -EACCES;
- }
-out:
- return error;
-}
-
-static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct ncp_server *server = NCP_SERVER(dir);
- int error, result, len;
- __u8 __name[NCP_MAXPATHLEN + 1];
-
- ncp_dbg(1, "removing %pd2\n", dentry);
-
- len = sizeof(__name);
- error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
- dentry->d_name.len, !ncp_preserve_case(dir));
- if (error)
- goto out;
-
- result = ncp_del_file_or_subdir(server, dir, __name);
- switch (result) {
- case 0x00:
- error = 0;
- break;
- case 0x85: /* unauthorized to delete file */
- case 0x8A: /* unauthorized to delete file */
- error = -EACCES;
- break;
- case 0x8F:
- case 0x90: /* read only */
- error = -EPERM;
- break;
- case 0x9F: /* in use by another client */
- error = -EBUSY;
- break;
- case 0xA0: /* directory not empty */
- error = -ENOTEMPTY;
- break;
- case 0xFF: /* someone deleted file */
- error = -ENOENT;
- break;
- default:
- error = result < 0 ? result : -EACCES;
- break;
- }
-out:
- return error;
-}
-
-static int ncp_unlink(struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = d_inode(dentry);
- struct ncp_server *server;
- int error;
-
- server = NCP_SERVER(dir);
- ncp_dbg(1, "unlinking %pd2\n", dentry);
-
- /*
- * Check whether to close the file ...
- */
- if (inode) {
- ncp_vdbg("closing file\n");
- ncp_make_closed(inode);
- }
-
- error = ncp_del_file_or_subdir2(server, dentry);
-#ifdef CONFIG_NCPFS_STRONG
- /* 9C is Invalid path.. It should be 8F, 90 - read only, but
- it is not :-( */
- if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
- error = ncp_force_unlink(dir, dentry);
- }
-#endif
- switch (error) {
- case 0x00:
- ncp_dbg(1, "removed %pd2\n", dentry);
- break;
- case 0x85:
- case 0x8A:
- error = -EACCES;
- break;
- case 0x8D: /* some files in use */
- case 0x8E: /* all files in use */
- error = -EBUSY;
- break;
- case 0x8F: /* some read only */
- case 0x90: /* all read only */
- case 0x9C: /* !!! returned when in-use or read-only by NW4 */
- error = -EPERM;
- break;
- case 0xFF:
- error = -ENOENT;
- break;
- default:
- error = error < 0 ? error : -EACCES;
- break;
- }
- return error;
-}
-
-static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- unsigned int flags)
-{
- struct ncp_server *server = NCP_SERVER(old_dir);
- int error;
- int old_len, new_len;
- __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
-
- if (flags)
- return -EINVAL;
-
- ncp_dbg(1, "%pd2 to %pd2\n", old_dentry, new_dentry);
-
- ncp_age_dentry(server, old_dentry);
- ncp_age_dentry(server, new_dentry);
-
- old_len = sizeof(__old_name);
- error = ncp_io2vol(server, __old_name, &old_len,
- old_dentry->d_name.name, old_dentry->d_name.len,
- !ncp_preserve_case(old_dir));
- if (error)
- goto out;
-
- new_len = sizeof(__new_name);
- error = ncp_io2vol(server, __new_name, &new_len,
- new_dentry->d_name.name, new_dentry->d_name.len,
- !ncp_preserve_case(new_dir));
- if (error)
- goto out;
-
- error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
- new_dir, __new_name);
-#ifdef CONFIG_NCPFS_STRONG
- if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
- server->m.flags & NCP_MOUNT_STRONG) { /* RO */
- error = ncp_force_rename(old_dir, old_dentry, __old_name,
- new_dir, new_dentry, __new_name);
- }
-#endif
- switch (error) {
- case 0x00:
- ncp_dbg(1, "renamed %pd -> %pd\n",
- old_dentry, new_dentry);
- ncp_d_prune(old_dentry);
- ncp_d_prune(new_dentry);
- break;
- case 0x9E:
- error = -ENAMETOOLONG;
- break;
- case 0xFF:
- error = -ENOENT;
- break;
- default:
- error = error < 0 ? error : -EACCES;
- break;
- }
-out:
- return error;
-}
-
-static int ncp_mknod(struct inode * dir, struct dentry *dentry,
- umode_t mode, dev_t rdev)
-{
- if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
- ncp_dbg(1, "mode = 0%ho\n", mode);
- return ncp_create_new(dir, dentry, mode, rdev, 0);
- }
- return -EPERM; /* Strange, but true */
-}
-
-/* The following routines are taken directly from msdos-fs */
-
-/* Linear day numbers of the respective 1sts in non-leap years. */
-
-static int day_n[] =
-{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
-/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
-
-static int utc2local(int time)
-{
- return time - sys_tz.tz_minuteswest * 60;
-}
-
-static int local2utc(int time)
-{
- return time + sys_tz.tz_minuteswest * 60;
-}
-
-/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
-int
-ncp_date_dos2unix(__le16 t, __le16 d)
-{
- unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d);
- int month, year, secs;
-
- /* first subtract and mask after that... Otherwise, if
- date == 0, bad things happen */
- month = ((date >> 5) - 1) & 15;
- year = date >> 9;
- secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
- 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
- year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
- /* days since 1.1.70 plus 80's leap day */
- return local2utc(secs);
-}
-
-
-/* Convert linear UNIX date to a MS-DOS time/date pair. */
-void
-ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
-{
- int day, year, nl_day, month;
-
- unix_date = utc2local(unix_date);
- *time = cpu_to_le16(
- (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
- (((unix_date / 3600) % 24) << 11));
- day = unix_date / 86400 - 3652;
- year = day / 365;
- if ((year + 3) / 4 + 365 * year > day)
- year--;
- day -= (year + 3) / 4 + 365 * year;
- if (day == 59 && !(year & 3)) {
- nl_day = day;
- month = 2;
- } else {
- nl_day = (year & 3) || day <= 59 ? day : day - 1;
- for (month = 1; month < 12; month++)
- if (day_n[month] > nl_day)
- break;
- }
- *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
-}
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
deleted file mode 100644
index 8f8cc0334ddd..000000000000
--- a/fs/ncpfs/file.c
+++ /dev/null
@@ -1,263 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * file.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/uaccess.h>
-
-#include <linux/time.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-
-#include "ncp_fs.h"
-
-static int ncp_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- return file_write_and_wait_range(file, start, end);
-}
-
-/*
- * Open a file with the specified read/write mode.
- */
-int ncp_make_open(struct inode *inode, int right)
-{
- int error;
- int access;
-
- error = -EINVAL;
- if (!inode) {
- pr_err("%s: got NULL inode\n", __func__);
- goto out;
- }
-
- ncp_dbg(1, "opened=%d, volume # %u, dir entry # %u\n",
- atomic_read(&NCP_FINFO(inode)->opened),
- NCP_FINFO(inode)->volNumber,
- NCP_FINFO(inode)->dirEntNum);
- error = -EACCES;
- mutex_lock(&NCP_FINFO(inode)->open_mutex);
- if (!atomic_read(&NCP_FINFO(inode)->opened)) {
- struct ncp_entry_info finfo;
- int result;
-
- /* tries max. rights */
- finfo.access = O_RDWR;
- result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
- inode, NULL, OC_MODE_OPEN,
- 0, AR_READ | AR_WRITE, &finfo);
- if (!result)
- goto update;
- /* RDWR did not succeeded, try readonly or writeonly as requested */
- switch (right) {
- case O_RDONLY:
- finfo.access = O_RDONLY;
- result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
- inode, NULL, OC_MODE_OPEN,
- 0, AR_READ, &finfo);
- break;
- case O_WRONLY:
- finfo.access = O_WRONLY;
- result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
- inode, NULL, OC_MODE_OPEN,
- 0, AR_WRITE, &finfo);
- break;
- }
- if (result) {
- ncp_vdbg("failed, result=%d\n", result);
- goto out_unlock;
- }
- /*
- * Update the inode information.
- */
- update:
- ncp_update_inode(inode, &finfo);
- atomic_set(&NCP_FINFO(inode)->opened, 1);
- }
-
- access = NCP_FINFO(inode)->access;
- ncp_vdbg("file open, access=%x\n", access);
- if (access == right || access == O_RDWR) {
- atomic_inc(&NCP_FINFO(inode)->opened);
- error = 0;
- }
-
-out_unlock:
- mutex_unlock(&NCP_FINFO(inode)->open_mutex);
-out:
- return error;
-}
-
-static ssize_t
-ncp_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
-{
- struct file *file = iocb->ki_filp;
- struct inode *inode = file_inode(file);
- size_t already_read = 0;
- off_t pos = iocb->ki_pos;
- size_t bufsize;
- int error;
- void *freepage;
- size_t freelen;
-
- ncp_dbg(1, "enter %pD2\n", file);
-
- if (!iov_iter_count(to))
- return 0;
- if (pos > inode->i_sb->s_maxbytes)
- return 0;
- iov_iter_truncate(to, inode->i_sb->s_maxbytes - pos);
-
- error = ncp_make_open(inode, O_RDONLY);
- if (error) {
- ncp_dbg(1, "open failed, error=%d\n", error);
- return error;
- }
-
- bufsize = NCP_SERVER(inode)->buffer_size;
-
- error = -EIO;
- freelen = ncp_read_bounce_size(bufsize);
- freepage = vmalloc(freelen);
- if (!freepage)
- goto outrel;
- error = 0;
- /* First read in as much as possible for each bufsize. */
- while (iov_iter_count(to)) {
- int read_this_time;
- size_t to_read = min_t(size_t,
- bufsize - (pos % bufsize),
- iov_iter_count(to));
-
- error = ncp_read_bounce(NCP_SERVER(inode),
- NCP_FINFO(inode)->file_handle,
- pos, to_read, to, &read_this_time,
- freepage, freelen);
- if (error) {
- error = -EIO; /* NW errno -> Linux errno */
- break;
- }
- pos += read_this_time;
- already_read += read_this_time;
-
- if (read_this_time != to_read)
- break;
- }
- vfree(freepage);
-
- iocb->ki_pos = pos;
-
- file_accessed(file);
-
- ncp_dbg(1, "exit %pD2\n", file);
-outrel:
- ncp_inode_close(inode);
- return already_read ? already_read : error;
-}
-
-static ssize_t
-ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
-{
- struct file *file = iocb->ki_filp;
- struct inode *inode = file_inode(file);
- size_t already_written = 0;
- size_t bufsize;
- int errno;
- void *bouncebuffer;
- off_t pos;
-
- ncp_dbg(1, "enter %pD2\n", file);
- errno = generic_write_checks(iocb, from);
- if (errno <= 0)
- return errno;
-
- errno = ncp_make_open(inode, O_WRONLY);
- if (errno) {
- ncp_dbg(1, "open failed, error=%d\n", errno);
- return errno;
- }
- bufsize = NCP_SERVER(inode)->buffer_size;
-
- errno = file_update_time(file);
- if (errno)
- goto outrel;
-
- bouncebuffer = vmalloc(bufsize);
- if (!bouncebuffer) {
- errno = -EIO; /* -ENOMEM */
- goto outrel;
- }
- pos = iocb->ki_pos;
- while (iov_iter_count(from)) {
- int written_this_time;
- size_t to_write = min_t(size_t,
- bufsize - (pos % bufsize),
- iov_iter_count(from));
-
- if (!copy_from_iter_full(bouncebuffer, to_write, from)) {
- errno = -EFAULT;
- break;
- }
- if (ncp_write_kernel(NCP_SERVER(inode),
- NCP_FINFO(inode)->file_handle,
- pos, to_write, bouncebuffer, &written_this_time) != 0) {
- errno = -EIO;
- break;
- }
- pos += written_this_time;
- already_written += written_this_time;
-
- if (written_this_time != to_write)
- break;
- }
- vfree(bouncebuffer);
-
- iocb->ki_pos = pos;
-
- if (pos > i_size_read(inode)) {
- inode_lock(inode);
- if (pos > i_size_read(inode))
- i_size_write(inode, pos);
- inode_unlock(inode);
- }
- ncp_dbg(1, "exit %pD2\n", file);
-outrel:
- ncp_inode_close(inode);
- return already_written ? already_written : errno;
-}
-
-static int ncp_release(struct inode *inode, struct file *file) {
- if (ncp_make_closed(inode)) {
- ncp_dbg(1, "failed to close\n");
- }
- return 0;
-}
-
-const struct file_operations ncp_file_operations =
-{
- .llseek = generic_file_llseek,
- .read_iter = ncp_file_read_iter,
- .write_iter = ncp_file_write_iter,
- .unlocked_ioctl = ncp_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ncp_compat_ioctl,
-#endif
- .mmap = ncp_mmap,
- .release = ncp_release,
- .fsync = ncp_fsync,
-};
-
-const struct inode_operations ncp_file_inode_operations =
-{
- .setattr = ncp_notify_change,
-};
diff --git a/fs/ncpfs/getopt.c b/fs/ncpfs/getopt.c
deleted file mode 100644
index 5c941bef14c4..000000000000
--- a/fs/ncpfs/getopt.c
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * getopt.c
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-
-#include <asm/errno.h>
-
-#include "getopt.h"
-
-/**
- * ncp_getopt - option parser
- * @caller: name of the caller, for error messages
- * @options: the options string
- * @opts: an array of &struct option entries controlling parser operations
- * @optopt: output; will contain the current option
- * @optarg: output; will contain the value (if one exists)
- * @value: output; may be NULL; will be overwritten with the integer value
- * of the current argument.
- *
- * Helper to parse options on the format used by mount ("a=b,c=d,e,f").
- * Returns opts->val if a matching entry in the 'opts' array is found,
- * 0 when no more tokens are found, -1 if an error is encountered.
- */
-int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts,
- char **optopt, char **optarg, unsigned long *value)
-{
- char *token;
- char *val;
-
- do {
- if ((token = strsep(options, ",")) == NULL)
- return 0;
- } while (*token == '\0');
- if (optopt)
- *optopt = token;
-
- if ((val = strchr (token, '=')) != NULL) {
- *val++ = 0;
- }
- *optarg = val;
- for (; opts->name; opts++) {
- if (!strcmp(opts->name, token)) {
- if (!val) {
- if (opts->has_arg & OPT_NOPARAM) {
- return opts->val;
- }
- pr_info("%s: the %s option requires an argument\n",
- caller, token);
- return -EINVAL;
- }
- if (opts->has_arg & OPT_INT) {
- int rc = kstrtoul(val, 0, value);
-
- if (rc) {
- pr_info("%s: invalid numeric value in %s=%s\n",
- caller, token, val);
- return rc;
- }
- return opts->val;
- }
- if (opts->has_arg & OPT_STRING) {
- return opts->val;
- }
- pr_info("%s: unexpected argument %s to the %s option\n",
- caller, val, token);
- return -EINVAL;
- }
- }
- pr_info("%s: Unrecognized mount option %s\n", caller, token);
- return -EOPNOTSUPP;
-}
diff --git a/fs/ncpfs/getopt.h b/fs/ncpfs/getopt.h
deleted file mode 100644
index 30f0da317670..000000000000
--- a/fs/ncpfs/getopt.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_GETOPT_H
-#define _LINUX_GETOPT_H
-
-#define OPT_NOPARAM 1
-#define OPT_INT 2
-#define OPT_STRING 4
-struct ncp_option {
- const char *name;
- unsigned int has_arg;
- int val;
-};
-
-extern int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts,
- char **optopt, char **optarg, unsigned long *value);
-
-#endif /* _LINUX_GETOPT_H */
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
deleted file mode 100644
index 41de88cdc053..000000000000
--- a/fs/ncpfs/inode.c
+++ /dev/null
@@ -1,1066 +0,0 @@
-/*
- * inode.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified for big endian by J.F. Chadima and David S. Miller
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- * Modified 1998 Wolfram Pienkoss for NLS
- * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-
-#include <linux/uaccess.h>
-#include <asm/byteorder.h>
-
-#include <linux/time.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/vfs.h>
-#include <linux/mount.h>
-#include <linux/seq_file.h>
-#include <linux/sched/signal.h>
-#include <linux/namei.h>
-
-#include <net/sock.h>
-
-#include "ncp_fs.h"
-#include "getopt.h"
-
-#define NCP_DEFAULT_FILE_MODE 0600
-#define NCP_DEFAULT_DIR_MODE 0700
-#define NCP_DEFAULT_TIME_OUT 10
-#define NCP_DEFAULT_RETRY_COUNT 20
-
-static void ncp_evict_inode(struct inode *);
-static void ncp_put_super(struct super_block *);
-static int ncp_statfs(struct dentry *, struct kstatfs *);
-static int ncp_show_options(struct seq_file *, struct dentry *);
-
-static struct kmem_cache * ncp_inode_cachep;
-
-static struct inode *ncp_alloc_inode(struct super_block *sb)
-{
- struct ncp_inode_info *ei;
- ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, GFP_KERNEL);
- if (!ei)
- return NULL;
- return &ei->vfs_inode;
-}
-
-static void ncp_i_callback(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode));
-}
-
-static void ncp_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, ncp_i_callback);
-}
-
-static void init_once(void *foo)
-{
- struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
-
- mutex_init(&ei->open_mutex);
- inode_init_once(&ei->vfs_inode);
-}
-
-static int init_inodecache(void)
-{
- ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
- sizeof(struct ncp_inode_info),
- 0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_ACCOUNT),
- init_once);
- if (ncp_inode_cachep == NULL)
- return -ENOMEM;
- return 0;
-}
-
-static void destroy_inodecache(void)
-{
- /*
- * Make sure all delayed rcu free inodes are flushed before we
- * destroy cache.
- */
- rcu_barrier();
- kmem_cache_destroy(ncp_inode_cachep);
-}
-
-static int ncp_remount(struct super_block *sb, int *flags, char* data)
-{
- sync_filesystem(sb);
- *flags |= SB_NODIRATIME;
- return 0;
-}
-
-static const struct super_operations ncp_sops =
-{
- .alloc_inode = ncp_alloc_inode,
- .destroy_inode = ncp_destroy_inode,
- .drop_inode = generic_delete_inode,
- .evict_inode = ncp_evict_inode,
- .put_super = ncp_put_super,
- .statfs = ncp_statfs,
- .remount_fs = ncp_remount,
- .show_options = ncp_show_options,
-};
-
-/*
- * Fill in the ncpfs-specific information in the inode.
- */
-static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo)
-{
- NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
- NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
- NCP_FINFO(inode)->volNumber = nwinfo->volume;
-}
-
-void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
-{
- ncp_update_dirent(inode, nwinfo);
- NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
- NCP_FINFO(inode)->access = nwinfo->access;
- memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
- sizeof(nwinfo->file_handle));
- ncp_dbg(1, "updated %s, volnum=%d, dirent=%u\n",
- nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
- NCP_FINFO(inode)->dirEntNum);
-}
-
-static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi)
-{
- /* NFS namespace mode overrides others if it's set. */
- ncp_dbg(1, "(%s) nfs.mode=0%o\n", nwi->entryName, nwi->nfs.mode);
- if (nwi->nfs.mode) {
- /* XXX Security? */
- inode->i_mode = nwi->nfs.mode;
- }
-
- inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
-
- inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate);
- inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate);
- inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate);
- inode->i_atime.tv_nsec = 0;
- inode->i_mtime.tv_nsec = 0;
- inode->i_ctime.tv_nsec = 0;
-}
-
-static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo)
-{
- struct nw_info_struct *nwi = &nwinfo->i;
- struct ncp_server *server = NCP_SERVER(inode);
-
- if (nwi->attributes & aDIR) {
- inode->i_mode = server->m.dir_mode;
- /* for directories dataStreamSize seems to be some
- Object ID ??? */
- i_size_write(inode, NCP_BLOCK_SIZE);
- } else {
- u32 size;
-
- inode->i_mode = server->m.file_mode;
- size = le32_to_cpu(nwi->dataStreamSize);
- i_size_write(inode, size);
-#ifdef CONFIG_NCPFS_EXTRAS
- if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
- && (nwi->attributes & aSHARED)) {
- switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
- case aHIDDEN:
- if (server->m.flags & NCP_MOUNT_SYMLINKS) {
- if (/* (size >= NCP_MIN_SYMLINK_SIZE)
- && */ (size <= NCP_MAX_SYMLINK_SIZE)) {
- inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
- NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK;
- break;
- }
- }
- /* FALLTHROUGH */
- case 0:
- if (server->m.flags & NCP_MOUNT_EXTRAS)
- inode->i_mode |= S_IRUGO;
- break;
- case aSYSTEM:
- if (server->m.flags & NCP_MOUNT_EXTRAS)
- inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO;
- break;
- /* case aSYSTEM|aHIDDEN: */
- default:
- /* reserved combination */
- break;
- }
- }
-#endif
- }
- if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO;
-}
-
-void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
-{
- NCP_FINFO(inode)->flags = 0;
- if (!atomic_read(&NCP_FINFO(inode)->opened)) {
- NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
- ncp_update_attrs(inode, nwinfo);
- }
-
- ncp_update_dates(inode, &nwinfo->i);
- ncp_update_dirent(inode, nwinfo);
-}
-
-/*
- * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes.
- */
-static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
-{
- struct ncp_server *server = NCP_SERVER(inode);
-
- NCP_FINFO(inode)->flags = 0;
-
- ncp_update_attrs(inode, nwinfo);
-
- ncp_dbg(2, "inode->i_mode = %u\n", inode->i_mode);
-
- set_nlink(inode, 1);
- inode->i_uid = server->m.uid;
- inode->i_gid = server->m.gid;
-
- ncp_update_dates(inode, &nwinfo->i);
- ncp_update_inode(inode, nwinfo);
-}
-
-#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
-static const struct inode_operations ncp_symlink_inode_operations = {
- .get_link = page_get_link,
- .setattr = ncp_notify_change,
-};
-#endif
-
-/*
- * Get a new inode.
- */
-struct inode *
-ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
-{
- struct inode *inode;
-
- if (info == NULL) {
- pr_err("%s: info is NULL\n", __func__);
- return NULL;
- }
-
- inode = new_inode(sb);
- if (inode) {
- atomic_set(&NCP_FINFO(inode)->opened, info->opened);
-
- inode->i_ino = info->ino;
- ncp_set_attr(inode, info);
- if (S_ISREG(inode->i_mode)) {
- inode->i_op = &ncp_file_inode_operations;
- inode->i_fop = &ncp_file_operations;
- } else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &ncp_dir_inode_operations;
- inode->i_fop = &ncp_dir_operations;
-#ifdef CONFIG_NCPFS_NFS_NS
- } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
- init_special_inode(inode, inode->i_mode,
- new_decode_dev(info->i.nfs.rdev));
-#endif
-#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
- } else if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &ncp_symlink_inode_operations;
- inode_nohighmem(inode);
- inode->i_data.a_ops = &ncp_symlink_aops;
-#endif
- } else {
- make_bad_inode(inode);
- }
- insert_inode_hash(inode);
- } else
- pr_err("%s: iget failed!\n", __func__);
- return inode;
-}
-
-static void
-ncp_evict_inode(struct inode *inode)
-{
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
-
- if (S_ISDIR(inode->i_mode)) {
- ncp_dbg(2, "put directory %ld\n", inode->i_ino);
- }
-
- if (ncp_make_closed(inode) != 0) {
- /* We can't do anything but complain. */
- pr_err("%s: could not close\n", __func__);
- }
-}
-
-static void ncp_stop_tasks(struct ncp_server *server) {
- struct sock* sk = server->ncp_sock->sk;
-
- lock_sock(sk);
- sk->sk_error_report = server->error_report;
- sk->sk_data_ready = server->data_ready;
- sk->sk_write_space = server->write_space;
- release_sock(sk);
- del_timer_sync(&server->timeout_tm);
-
- flush_work(&server->rcv.tq);
- if (sk->sk_socket->type == SOCK_STREAM)
- flush_work(&server->tx.tq);
- else
- flush_work(&server->timeout_tq);
-}
-
-static int ncp_show_options(struct seq_file *seq, struct dentry *root)
-{
- struct ncp_server *server = NCP_SBP(root->d_sb);
- unsigned int tmp;
-
- if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
- seq_printf(seq, ",uid=%u",
- from_kuid_munged(&init_user_ns, server->m.uid));
- if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
- seq_printf(seq, ",gid=%u",
- from_kgid_munged(&init_user_ns, server->m.gid));
- if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
- seq_printf(seq, ",owner=%u",
- from_kuid_munged(&init_user_ns, server->m.mounted_uid));
- tmp = server->m.file_mode & S_IALLUGO;
- if (tmp != NCP_DEFAULT_FILE_MODE)
- seq_printf(seq, ",mode=0%o", tmp);
- tmp = server->m.dir_mode & S_IALLUGO;
- if (tmp != NCP_DEFAULT_DIR_MODE)
- seq_printf(seq, ",dirmode=0%o", tmp);
- if (server->m.time_out != NCP_DEFAULT_TIME_OUT * HZ / 100) {
- tmp = server->m.time_out * 100 / HZ;
- seq_printf(seq, ",timeout=%u", tmp);
- }
- if (server->m.retry_count != NCP_DEFAULT_RETRY_COUNT)
- seq_printf(seq, ",retry=%u", server->m.retry_count);
- if (server->m.flags != 0)
- seq_printf(seq, ",flags=%lu", server->m.flags);
- if (server->m.wdog_pid != NULL)
- seq_printf(seq, ",wdogpid=%u", pid_vnr(server->m.wdog_pid));
-
- return 0;
-}
-
-static const struct ncp_option ncp_opts[] = {
- { "uid", OPT_INT, 'u' },
- { "gid", OPT_INT, 'g' },
- { "owner", OPT_INT, 'o' },
- { "mode", OPT_INT, 'm' },
- { "dirmode", OPT_INT, 'd' },
- { "timeout", OPT_INT, 't' },
- { "retry", OPT_INT, 'r' },
- { "flags", OPT_INT, 'f' },
- { "wdogpid", OPT_INT, 'w' },
- { "ncpfd", OPT_INT, 'n' },
- { "infofd", OPT_INT, 'i' }, /* v5 */
- { "version", OPT_INT, 'v' },
- { NULL, 0, 0 } };
-
-static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) {
- int optval;
- char *optarg;
- unsigned long optint;
- int version = 0;
- int ret;
-
- data->flags = 0;
- data->int_flags = 0;
- data->mounted_uid = GLOBAL_ROOT_UID;
- data->wdog_pid = NULL;
- data->ncp_fd = ~0;
- data->time_out = NCP_DEFAULT_TIME_OUT;
- data->retry_count = NCP_DEFAULT_RETRY_COUNT;
- data->uid = GLOBAL_ROOT_UID;
- data->gid = GLOBAL_ROOT_GID;
- data->file_mode = NCP_DEFAULT_FILE_MODE;
- data->dir_mode = NCP_DEFAULT_DIR_MODE;
- data->info_fd = -1;
- data->mounted_vol[0] = 0;
-
- while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) {
- ret = optval;
- if (ret < 0)
- goto err;
- switch (optval) {
- case 'u':
- data->uid = make_kuid(current_user_ns(), optint);
- if (!uid_valid(data->uid)) {
- ret = -EINVAL;
- goto err;
- }
- break;
- case 'g':
- data->gid = make_kgid(current_user_ns(), optint);
- if (!gid_valid(data->gid)) {
- ret = -EINVAL;
- goto err;
- }
- break;
- case 'o':
- data->mounted_uid = make_kuid(current_user_ns(), optint);
- if (!uid_valid(data->mounted_uid)) {
- ret = -EINVAL;
- goto err;
- }
- break;
- case 'm':
- data->file_mode = optint;
- break;
- case 'd':
- data->dir_mode = optint;
- break;
- case 't':
- data->time_out = optint;
- break;
- case 'r':
- data->retry_count = optint;
- break;
- case 'f':
- data->flags = optint;
- break;
- case 'w':
- data->wdog_pid = find_get_pid(optint);
- break;
- case 'n':
- data->ncp_fd = optint;
- break;
- case 'i':
- data->info_fd = optint;
- break;
- case 'v':
- ret = -ECHRNG;
- if (optint < NCP_MOUNT_VERSION_V4)
- goto err;
- if (optint > NCP_MOUNT_VERSION_V5)
- goto err;
- version = optint;
- break;
-
- }
- }
- return 0;
-err:
- put_pid(data->wdog_pid);
- data->wdog_pid = NULL;
- return ret;
-}
-
-static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
-{
- struct ncp_mount_data_kernel data;
- struct ncp_server *server;
- struct inode *root_inode;
- struct socket *sock;
- int error;
- int default_bufsize;
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- int options;
-#endif
- struct ncp_entry_info finfo;
-
- memset(&data, 0, sizeof(data));
- server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL);
- if (!server)
- return -ENOMEM;
- sb->s_fs_info = server;
-
- error = -EFAULT;
- if (raw_data == NULL)
- goto out;
- switch (*(int*)raw_data) {
- case NCP_MOUNT_VERSION:
- {
- struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
-
- data.flags = md->flags;
- data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
- data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
- data.wdog_pid = find_get_pid(md->wdog_pid);
- data.ncp_fd = md->ncp_fd;
- data.time_out = md->time_out;
- data.retry_count = md->retry_count;
- data.uid = make_kuid(current_user_ns(), md->uid);
- data.gid = make_kgid(current_user_ns(), md->gid);
- data.file_mode = md->file_mode;
- data.dir_mode = md->dir_mode;
- data.info_fd = -1;
- memcpy(data.mounted_vol, md->mounted_vol,
- NCP_VOLNAME_LEN+1);
- }
- break;
- case NCP_MOUNT_VERSION_V4:
- {
- struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
-
- data.flags = md->flags;
- data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid);
- data.wdog_pid = find_get_pid(md->wdog_pid);
- data.ncp_fd = md->ncp_fd;
- data.time_out = md->time_out;
- data.retry_count = md->retry_count;
- data.uid = make_kuid(current_user_ns(), md->uid);
- data.gid = make_kgid(current_user_ns(), md->gid);
- data.file_mode = md->file_mode;
- data.dir_mode = md->dir_mode;
- data.info_fd = -1;
- }
- break;
- default:
- error = -ECHRNG;
- if (memcmp(raw_data, "vers", 4) == 0) {
- error = ncp_parse_options(&data, raw_data);
- }
- if (error)
- goto out;
- break;
- }
- error = -EINVAL;
- if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
- !gid_valid(data.gid))
- goto out;
- sock = sockfd_lookup(data.ncp_fd, &error);
- if (!sock)
- goto out;
-
- if (sock->type == SOCK_STREAM)
- default_bufsize = 0xF000;
- else
- default_bufsize = 1024;
-
- sb->s_flags |= SB_NODIRATIME; /* probably even noatime */
- sb->s_maxbytes = 0xFFFFFFFFU;
- sb->s_blocksize = 1024; /* Eh... Is this correct? */
- sb->s_blocksize_bits = 10;
- sb->s_magic = NCP_SUPER_MAGIC;
- sb->s_op = &ncp_sops;
- sb->s_d_op = &ncp_dentry_operations;
-
- server = NCP_SBP(sb);
- memset(server, 0, sizeof(*server));
-
- error = super_setup_bdi(sb);
- if (error)
- goto out_fput;
-
- server->ncp_sock = sock;
-
- if (data.info_fd != -1) {
- struct socket *info_sock = sockfd_lookup(data.info_fd, &error);
- if (!info_sock)
- goto out_fput;
- server->info_sock = info_sock;
- error = -EBADFD;
- if (info_sock->type != SOCK_STREAM)
- goto out_fput2;
- }
-
-/* server->lock = 0; */
- mutex_init(&server->mutex);
- server->packet = NULL;
-/* server->buffer_size = 0; */
-/* server->conn_status = 0; */
-/* server->root_dentry = NULL; */
-/* server->root_setuped = 0; */
- mutex_init(&server->root_setup_lock);
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
-/* server->sign_wanted = 0; */
-/* server->sign_active = 0; */
-#endif
- init_rwsem(&server->auth_rwsem);
- server->auth.auth_type = NCP_AUTH_NONE;
-/* server->auth.object_name_len = 0; */
-/* server->auth.object_name = NULL; */
-/* server->auth.object_type = 0; */
-/* server->priv.len = 0; */
-/* server->priv.data = NULL; */
-
- server->m = data;
- /* Although anything producing this is buggy, it happens
- now because of PATH_MAX changes.. */
- if (server->m.time_out < 1) {
- server->m.time_out = 10;
- pr_info("You need to recompile your ncpfs utils..\n");
- }
- server->m.time_out = server->m.time_out * HZ / 100;
- server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG;
- server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR;
-
-#ifdef CONFIG_NCPFS_NLS
- /* load the default NLS charsets */
- server->nls_vol = load_nls_default();
- server->nls_io = load_nls_default();
-#endif /* CONFIG_NCPFS_NLS */
-
- atomic_set(&server->dentry_ttl, 0); /* no caching */
-
- INIT_LIST_HEAD(&server->tx.requests);
- mutex_init(&server->rcv.creq_mutex);
- server->tx.creq = NULL;
- server->rcv.creq = NULL;
-
- timer_setup(&server->timeout_tm, ncpdgram_timeout_call, 0);
-#undef NCP_PACKET_SIZE
-#define NCP_PACKET_SIZE 131072
- error = -ENOMEM;
- server->packet_size = NCP_PACKET_SIZE;
- server->packet = vmalloc(NCP_PACKET_SIZE);
- if (server->packet == NULL)
- goto out_nls;
- server->txbuf = vmalloc(NCP_PACKET_SIZE);
- if (server->txbuf == NULL)
- goto out_packet;
- server->rxbuf = vmalloc(NCP_PACKET_SIZE);
- if (server->rxbuf == NULL)
- goto out_txbuf;
-
- lock_sock(sock->sk);
- server->data_ready = sock->sk->sk_data_ready;
- server->write_space = sock->sk->sk_write_space;
- server->error_report = sock->sk->sk_error_report;
- sock->sk->sk_user_data = server;
- sock->sk->sk_data_ready = ncp_tcp_data_ready;
- sock->sk->sk_error_report = ncp_tcp_error_report;
- if (sock->type == SOCK_STREAM) {
- server->rcv.ptr = (unsigned char*)&server->rcv.buf;
- server->rcv.len = 10;
- server->rcv.state = 0;
- INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc);
- INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc);
- sock->sk->sk_write_space = ncp_tcp_write_space;
- } else {
- INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc);
- INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc);
- }
- release_sock(sock->sk);
-
- ncp_lock_server(server);
- error = ncp_connect(server);
- ncp_unlock_server(server);
- if (error < 0)
- goto out_rxbuf;
- ncp_dbg(1, "NCP_SBP(sb) = %p\n", NCP_SBP(sb));
-
- error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- if (ncp_negotiate_size_and_options(server, default_bufsize,
- NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
- {
- if (options != NCP_DEFAULT_OPTIONS)
- {
- if (ncp_negotiate_size_and_options(server,
- default_bufsize,
- options & 2,
- &(server->buffer_size), &options) != 0)
-
- {
- goto out_disconnect;
- }
- }
- ncp_lock_server(server);
- if (options & 2)
- server->sign_wanted = 1;
- ncp_unlock_server(server);
- }
- else
-#endif /* CONFIG_NCPFS_PACKET_SIGNING */
- if (ncp_negotiate_buffersize(server, default_bufsize,
- &(server->buffer_size)) != 0)
- goto out_disconnect;
- ncp_dbg(1, "bufsize = %d\n", server->buffer_size);
-
- memset(&finfo, 0, sizeof(finfo));
- finfo.i.attributes = aDIR;
- finfo.i.dataStreamSize = 0; /* ignored */
- finfo.i.dirEntNum = 0;
- finfo.i.DosDirNum = 0;
-#ifdef CONFIG_NCPFS_SMALLDOS
- finfo.i.NSCreator = NW_NS_DOS;
-#endif
- finfo.volume = NCP_NUMBER_OF_VOLUMES;
- /* set dates of mountpoint to Jan 1, 1986; 00:00 */
- finfo.i.creationTime = finfo.i.modifyTime
- = cpu_to_le16(0x0000);
- finfo.i.creationDate = finfo.i.modifyDate
- = finfo.i.lastAccessDate
- = cpu_to_le16(0x0C21);
- finfo.i.nameLen = 0;
- finfo.i.entryName[0] = '\0';
-
- finfo.opened = 0;
- finfo.ino = 2; /* tradition */
-
- server->name_space[finfo.volume] = NW_NS_DOS;
-
- error = -ENOMEM;
- root_inode = ncp_iget(sb, &finfo);
- if (!root_inode)
- goto out_disconnect;
- ncp_dbg(1, "root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
- sb->s_root = d_make_root(root_inode);
- if (!sb->s_root)
- goto out_disconnect;
- return 0;
-
-out_disconnect:
- ncp_lock_server(server);
- ncp_disconnect(server);
- ncp_unlock_server(server);
-out_rxbuf:
- ncp_stop_tasks(server);
- vfree(server->rxbuf);
-out_txbuf:
- vfree(server->txbuf);
-out_packet:
- vfree(server->packet);
-out_nls:
-#ifdef CONFIG_NCPFS_NLS
- unload_nls(server->nls_io);
- unload_nls(server->nls_vol);
-#endif
- mutex_destroy(&server->rcv.creq_mutex);
- mutex_destroy(&server->root_setup_lock);
- mutex_destroy(&server->mutex);
-out_fput2:
- if (server->info_sock)
- sockfd_put(server->info_sock);
-out_fput:
- sockfd_put(sock);
-out:
- put_pid(data.wdog_pid);
- sb->s_fs_info = NULL;
- kfree(server);
- return error;
-}
-
-static void delayed_free(struct rcu_head *p)
-{
- struct ncp_server *server = container_of(p, struct ncp_server, rcu);
-#ifdef CONFIG_NCPFS_NLS
- /* unload the NLS charsets */
- unload_nls(server->nls_vol);
- unload_nls(server->nls_io);
-#endif /* CONFIG_NCPFS_NLS */
- kfree(server);
-}
-
-static void ncp_put_super(struct super_block *sb)
-{
- struct ncp_server *server = NCP_SBP(sb);
-
- ncp_lock_server(server);
- ncp_disconnect(server);
- ncp_unlock_server(server);
-
- ncp_stop_tasks(server);
-
- mutex_destroy(&server->rcv.creq_mutex);
- mutex_destroy(&server->root_setup_lock);
- mutex_destroy(&server->mutex);
-
- if (server->info_sock)
- sockfd_put(server->info_sock);
- sockfd_put(server->ncp_sock);
- kill_pid(server->m.wdog_pid, SIGTERM, 1);
- put_pid(server->m.wdog_pid);
-
- kfree(server->priv.data);
- kfree(server->auth.object_name);
- vfree(server->rxbuf);
- vfree(server->txbuf);
- vfree(server->packet);
- call_rcu(&server->rcu, delayed_free);
-}
-
-static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf)
-{
- struct dentry* d;
- struct inode* i;
- struct ncp_inode_info* ni;
- struct ncp_server* s;
- struct ncp_volume_info vi;
- struct super_block *sb = dentry->d_sb;
- int err;
- __u8 dh;
-
- d = sb->s_root;
- if (!d) {
- goto dflt;
- }
- i = d_inode(d);
- if (!i) {
- goto dflt;
- }
- ni = NCP_FINFO(i);
- if (!ni) {
- goto dflt;
- }
- s = NCP_SBP(sb);
- if (!s) {
- goto dflt;
- }
- if (!s->m.mounted_vol[0]) {
- goto dflt;
- }
-
- err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh);
- if (err) {
- goto dflt;
- }
- err = ncp_get_directory_info(s, dh, &vi);
- ncp_dirhandle_free(s, dh);
- if (err) {
- goto dflt;
- }
- buf->f_type = NCP_SUPER_MAGIC;
- buf->f_bsize = vi.sectors_per_block * 512;
- buf->f_blocks = vi.total_blocks;
- buf->f_bfree = vi.free_blocks;
- buf->f_bavail = vi.free_blocks;
- buf->f_files = vi.total_dir_entries;
- buf->f_ffree = vi.available_dir_entries;
- buf->f_namelen = 12;
- return 0;
-
- /* We cannot say how much disk space is left on a mounted
- NetWare Server, because free space is distributed over
- volumes, and the current user might have disk quotas. So
- free space is not that simple to determine. Our decision
- here is to err conservatively. */
-
-dflt:;
- buf->f_type = NCP_SUPER_MAGIC;
- buf->f_bsize = NCP_BLOCK_SIZE;
- buf->f_blocks = 0;
- buf->f_bfree = 0;
- buf->f_bavail = 0;
- buf->f_namelen = 12;
- return 0;
-}
-
-int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
-{
- struct inode *inode = d_inode(dentry);
- int result = 0;
- __le32 info_mask;
- struct nw_modify_dos_info info;
- struct ncp_server *server;
-
- result = -EIO;
-
- server = NCP_SERVER(inode);
- if (!server) /* How this could happen? */
- goto out;
-
- result = -EPERM;
- if (IS_DEADDIR(d_inode(dentry)))
- goto out;
-
- /* ageing the dentry to force validation */
- ncp_age_dentry(server, dentry);
-
- result = setattr_prepare(dentry, attr);
- if (result < 0)
- goto out;
-
- result = -EPERM;
- if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid))
- goto out;
-
- if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid))
- goto out;
-
- if (((attr->ia_valid & ATTR_MODE) &&
- (attr->ia_mode &
- ~(S_IFREG | S_IFDIR | S_IRWXUGO))))
- goto out;
-
- info_mask = 0;
- memset(&info, 0, sizeof(info));
-
-#if 1
- if ((attr->ia_valid & ATTR_MODE) != 0)
- {
- umode_t newmode = attr->ia_mode;
-
- info_mask |= DM_ATTRIBUTES;
-
- if (S_ISDIR(inode->i_mode)) {
- newmode &= server->m.dir_mode;
- } else {
-#ifdef CONFIG_NCPFS_EXTRAS
- if (server->m.flags & NCP_MOUNT_EXTRAS) {
- /* any non-default execute bit set */
- if (newmode & ~server->m.file_mode & S_IXUGO)
- info.attributes |= aSHARED | aSYSTEM;
- /* read for group/world and not in default file_mode */
- else if (newmode & ~server->m.file_mode & S_IRUGO)
- info.attributes |= aSHARED;
- } else
-#endif
- newmode &= server->m.file_mode;
- }
- if (newmode & S_IWUGO)
- info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
- else
- info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
-
-#ifdef CONFIG_NCPFS_NFS_NS
- if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) {
- result = ncp_modify_nfs_info(server,
- NCP_FINFO(inode)->volNumber,
- NCP_FINFO(inode)->dirEntNum,
- attr->ia_mode, 0);
- if (result != 0)
- goto out;
- info.attributes &= ~(aSHARED | aSYSTEM);
- {
- /* mark partial success */
- struct iattr tmpattr;
-
- tmpattr.ia_valid = ATTR_MODE;
- tmpattr.ia_mode = attr->ia_mode;
-
- setattr_copy(inode, &tmpattr);
- mark_inode_dirty(inode);
- }
- }
-#endif
- }
-#endif
-
- /* Do SIZE before attributes, otherwise mtime together with size does not work...
- */
- if ((attr->ia_valid & ATTR_SIZE) != 0) {
- int written;
-
- ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size);
-
- if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
- result = -EACCES;
- goto out;
- }
- ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
- attr->ia_size, 0, "", &written);
-
- /* According to ndir, the changes only take effect after
- closing the file */
- ncp_inode_close(inode);
- result = ncp_make_closed(inode);
- if (result)
- goto out;
-
- if (attr->ia_size != i_size_read(inode)) {
- truncate_setsize(inode, attr->ia_size);
- mark_inode_dirty(inode);
- }
- }
- if ((attr->ia_valid & ATTR_CTIME) != 0) {
- info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
- ncp_date_unix2dos(attr->ia_ctime.tv_sec,
- &info.creationTime, &info.creationDate);
- }
- if ((attr->ia_valid & ATTR_MTIME) != 0) {
- info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
- ncp_date_unix2dos(attr->ia_mtime.tv_sec,
- &info.modifyTime, &info.modifyDate);
- }
- if ((attr->ia_valid & ATTR_ATIME) != 0) {
- __le16 dummy;
- info_mask |= (DM_LAST_ACCESS_DATE);
- ncp_date_unix2dos(attr->ia_atime.tv_sec,
- &dummy, &info.lastAccessDate);
- }
- if (info_mask != 0) {
- result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
- inode, info_mask, &info);
- if (result != 0) {
- if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
- /* NetWare seems not to allow this. I
- do not know why. So, just tell the
- user everything went fine. This is
- a terrible hack, but I do not know
- how to do this correctly. */
- result = 0;
- } else
- goto out;
- }
-#ifdef CONFIG_NCPFS_STRONG
- if ((!result) && (info_mask & DM_ATTRIBUTES))
- NCP_FINFO(inode)->nwattr = info.attributes;
-#endif
- }
- if (result)
- goto out;
-
- setattr_copy(inode, attr);
- mark_inode_dirty(inode);
-
-out:
- if (result > 0)
- result = -EACCES;
- return result;
-}
-
-static struct dentry *ncp_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
-{
- return mount_nodev(fs_type, flags, data, ncp_fill_super);
-}
-
-static struct file_system_type ncp_fs_type = {
- .owner = THIS_MODULE,
- .name = "ncpfs",
- .mount = ncp_mount,
- .kill_sb = kill_anon_super,
- .fs_flags = FS_BINARY_MOUNTDATA,
-};
-MODULE_ALIAS_FS("ncpfs");
-
-static int __init init_ncp_fs(void)
-{
- int err;
- ncp_dbg(1, "called\n");
-
- err = init_inodecache();
- if (err)
- goto out1;
- err = register_filesystem(&ncp_fs_type);
- if (err)
- goto out;
- return 0;
-out:
- destroy_inodecache();
-out1:
- return err;
-}
-
-static void __exit exit_ncp_fs(void)
-{
- ncp_dbg(1, "called\n");
- unregister_filesystem(&ncp_fs_type);
- destroy_inodecache();
-}
-
-module_init(init_ncp_fs)
-module_exit(exit_ncp_fs)
-MODULE_LICENSE("GPL");
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
deleted file mode 100644
index d378b98cd7b6..000000000000
--- a/fs/ncpfs/ioctl.c
+++ /dev/null
@@ -1,923 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ioctl.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- * Modified 1998, 1999 Wolfram Pienkoss for NLS
- *
- */
-
-#include <linux/capability.h>
-#include <linux/compat.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <linux/time.h>
-#include <linux/mm.h>
-#include <linux/mount.h>
-#include <linux/slab.h>
-#include <linux/highuid.h>
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/cred.h>
-
-#include <linux/uaccess.h>
-
-#include "ncp_fs.h"
-
-/* maximum limit for ncp_objectname_ioctl */
-#define NCP_OBJECT_NAME_MAX_LEN 4096
-/* maximum limit for ncp_privatedata_ioctl */
-#define NCP_PRIVATE_DATA_MAX_LEN 8192
-/* maximum negotiable packet size */
-#define NCP_PACKET_SIZE_INTERNAL 65536
-
-static int
-ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
- struct ncp_fs_info __user *arg)
-{
- struct ncp_fs_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (info.version != NCP_GET_FS_INFO_VERSION) {
- ncp_dbg(1, "info.version invalid: %d\n", info.version);
- return -EINVAL;
- }
- /* TODO: info.addr = server->m.serv_addr; */
- SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
- info.connection = server->connection;
- info.buffer_size = server->buffer_size;
- info.volume_number = NCP_FINFO(inode)->volNumber;
- info.directory_id = NCP_FINFO(inode)->DosDirNum;
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int
-ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
- struct ncp_fs_info_v2 __user * arg)
-{
- struct ncp_fs_info_v2 info2;
-
- if (copy_from_user(&info2, arg, sizeof(info2)))
- return -EFAULT;
-
- if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
- ncp_dbg(1, "info.version invalid: %d\n", info2.version);
- return -EINVAL;
- }
- info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
- info2.connection = server->connection;
- info2.buffer_size = server->buffer_size;
- info2.volume_number = NCP_FINFO(inode)->volNumber;
- info2.directory_id = NCP_FINFO(inode)->DosDirNum;
- info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
-
- if (copy_to_user(arg, &info2, sizeof(info2)))
- return -EFAULT;
- return 0;
-}
-
-#ifdef CONFIG_COMPAT
-struct compat_ncp_objectname_ioctl
-{
- s32 auth_type;
- u32 object_name_len;
- compat_caddr_t object_name; /* a userspace data, in most cases user name */
-};
-
-struct compat_ncp_fs_info_v2 {
- s32 version;
- u32 mounted_uid;
- u32 connection;
- u32 buffer_size;
-
- u32 volume_number;
- u32 directory_id;
-
- u32 dummy1;
- u32 dummy2;
- u32 dummy3;
-};
-
-struct compat_ncp_ioctl_request {
- u32 function;
- u32 size;
- compat_caddr_t data;
-};
-
-struct compat_ncp_privatedata_ioctl
-{
- u32 len;
- compat_caddr_t data; /* ~1000 for NDS */
-};
-
-#define NCP_IOC_GET_FS_INFO_V2_32 _IOWR('n', 4, struct compat_ncp_fs_info_v2)
-#define NCP_IOC_NCPREQUEST_32 _IOR('n', 1, struct compat_ncp_ioctl_request)
-#define NCP_IOC_GETOBJECTNAME_32 _IOWR('n', 9, struct compat_ncp_objectname_ioctl)
-#define NCP_IOC_SETOBJECTNAME_32 _IOR('n', 9, struct compat_ncp_objectname_ioctl)
-#define NCP_IOC_GETPRIVATEDATA_32 _IOWR('n', 10, struct compat_ncp_privatedata_ioctl)
-#define NCP_IOC_SETPRIVATEDATA_32 _IOR('n', 10, struct compat_ncp_privatedata_ioctl)
-
-static int
-ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
- struct compat_ncp_fs_info_v2 __user * arg)
-{
- struct compat_ncp_fs_info_v2 info2;
-
- if (copy_from_user(&info2, arg, sizeof(info2)))
- return -EFAULT;
-
- if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
- ncp_dbg(1, "info.version invalid: %d\n", info2.version);
- return -EINVAL;
- }
- info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
- info2.connection = server->connection;
- info2.buffer_size = server->buffer_size;
- info2.volume_number = NCP_FINFO(inode)->volNumber;
- info2.directory_id = NCP_FINFO(inode)->DosDirNum;
- info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
-
- if (copy_to_user(arg, &info2, sizeof(info2)))
- return -EFAULT;
- return 0;
-}
-#endif
-
-#define NCP_IOC_GETMOUNTUID16 _IOW('n', 2, u16)
-#define NCP_IOC_GETMOUNTUID32 _IOW('n', 2, u32)
-#define NCP_IOC_GETMOUNTUID64 _IOW('n', 2, u64)
-
-#ifdef CONFIG_NCPFS_NLS
-/* Here we are select the iocharset and the codepage for NLS.
- * Thanks Petr Vandrovec for idea and many hints.
- */
-static int
-ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
-{
- struct ncp_nls_ioctl user;
- struct nls_table *codepage;
- struct nls_table *iocharset;
- struct nls_table *oldset_io;
- struct nls_table *oldset_cp;
- int utf8;
- int err;
-
- if (copy_from_user(&user, arg, sizeof(user)))
- return -EFAULT;
-
- codepage = NULL;
- user.codepage[NCP_IOCSNAME_LEN] = 0;
- if (!user.codepage[0] || !strcmp(user.codepage, "default"))
- codepage = load_nls_default();
- else {
- codepage = load_nls(user.codepage);
- if (!codepage) {
- return -EBADRQC;
- }
- }
-
- iocharset = NULL;
- user.iocharset[NCP_IOCSNAME_LEN] = 0;
- if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) {
- iocharset = load_nls_default();
- utf8 = 0;
- } else if (!strcmp(user.iocharset, "utf8")) {
- iocharset = load_nls_default();
- utf8 = 1;
- } else {
- iocharset = load_nls(user.iocharset);
- if (!iocharset) {
- unload_nls(codepage);
- return -EBADRQC;
- }
- utf8 = 0;
- }
-
- mutex_lock(&server->root_setup_lock);
- if (server->root_setuped) {
- oldset_cp = codepage;
- oldset_io = iocharset;
- err = -EBUSY;
- } else {
- if (utf8)
- NCP_SET_FLAG(server, NCP_FLAG_UTF8);
- else
- NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
- oldset_cp = server->nls_vol;
- server->nls_vol = codepage;
- oldset_io = server->nls_io;
- server->nls_io = iocharset;
- err = 0;
- }
- mutex_unlock(&server->root_setup_lock);
- unload_nls(oldset_cp);
- unload_nls(oldset_io);
-
- return err;
-}
-
-static int
-ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
-{
- struct ncp_nls_ioctl user;
- int len;
-
- memset(&user, 0, sizeof(user));
- mutex_lock(&server->root_setup_lock);
- if (server->nls_vol && server->nls_vol->charset) {
- len = strlen(server->nls_vol->charset);
- if (len > NCP_IOCSNAME_LEN)
- len = NCP_IOCSNAME_LEN;
- strncpy(user.codepage, server->nls_vol->charset, len);
- user.codepage[len] = 0;
- }
-
- if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
- strcpy(user.iocharset, "utf8");
- else if (server->nls_io && server->nls_io->charset) {
- len = strlen(server->nls_io->charset);
- if (len > NCP_IOCSNAME_LEN)
- len = NCP_IOCSNAME_LEN;
- strncpy(user.iocharset, server->nls_io->charset, len);
- user.iocharset[len] = 0;
- }
- mutex_unlock(&server->root_setup_lock);
-
- if (copy_to_user(arg, &user, sizeof(user)))
- return -EFAULT;
- return 0;
-}
-#endif /* CONFIG_NCPFS_NLS */
-
-static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg)
-{
- struct ncp_server *server = NCP_SERVER(inode);
- int result;
- struct ncp_ioctl_request request;
- char* bouncebuffer;
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
-#ifdef CONFIG_COMPAT
- case NCP_IOC_NCPREQUEST_32:
-#endif
- case NCP_IOC_NCPREQUEST:
-#ifdef CONFIG_COMPAT
- if (cmd == NCP_IOC_NCPREQUEST_32) {
- struct compat_ncp_ioctl_request request32;
- if (copy_from_user(&request32, argp, sizeof(request32)))
- return -EFAULT;
- request.function = request32.function;
- request.size = request32.size;
- request.data = compat_ptr(request32.data);
- } else
-#endif
- if (copy_from_user(&request, argp, sizeof(request)))
- return -EFAULT;
-
- if ((request.function > 255)
- || (request.size >
- NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
- return -EINVAL;
- }
- bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
- if (!bouncebuffer)
- return -ENOMEM;
- if (copy_from_user(bouncebuffer, request.data, request.size)) {
- vfree(bouncebuffer);
- return -EFAULT;
- }
- ncp_lock_server(server);
-
- /* FIXME: We hack around in the server's structures
- here to be able to use ncp_request */
-
- server->has_subfunction = 0;
- server->current_size = request.size;
- memcpy(server->packet, bouncebuffer, request.size);
-
- result = ncp_request2(server, request.function,
- bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
- if (result < 0)
- result = -EIO;
- else
- result = server->reply_size;
- ncp_unlock_server(server);
- ncp_dbg(1, "copy %d bytes\n", result);
- if (result >= 0)
- if (copy_to_user(request.data, bouncebuffer, result))
- result = -EFAULT;
- vfree(bouncebuffer);
- return result;
-
- case NCP_IOC_CONN_LOGGED_IN:
-
- if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
- return -EINVAL;
- mutex_lock(&server->root_setup_lock);
- if (server->root_setuped)
- result = -EBUSY;
- else {
- result = ncp_conn_logged_in(inode->i_sb);
- if (result == 0)
- server->root_setuped = 1;
- }
- mutex_unlock(&server->root_setup_lock);
- return result;
-
- case NCP_IOC_GET_FS_INFO:
- return ncp_get_fs_info(server, inode, argp);
-
- case NCP_IOC_GET_FS_INFO_V2:
- return ncp_get_fs_info_v2(server, inode, argp);
-
-#ifdef CONFIG_COMPAT
- case NCP_IOC_GET_FS_INFO_V2_32:
- return ncp_get_compat_fs_info_v2(server, inode, argp);
-#endif
- /* we have too many combinations of CONFIG_COMPAT,
- * CONFIG_64BIT and CONFIG_UID16, so just handle
- * any of the possible ioctls */
- case NCP_IOC_GETMOUNTUID16:
- {
- u16 uid;
-
- SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
- if (put_user(uid, (u16 __user *)argp))
- return -EFAULT;
- return 0;
- }
- case NCP_IOC_GETMOUNTUID32:
- {
- uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
- if (put_user(uid, (u32 __user *)argp))
- return -EFAULT;
- return 0;
- }
- case NCP_IOC_GETMOUNTUID64:
- {
- uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
- if (put_user(uid, (u64 __user *)argp))
- return -EFAULT;
- return 0;
- }
- case NCP_IOC_GETROOT:
- {
- struct ncp_setroot_ioctl sr;
-
- result = -EACCES;
- mutex_lock(&server->root_setup_lock);
- if (server->m.mounted_vol[0]) {
- struct dentry* dentry = inode->i_sb->s_root;
-
- if (dentry) {
- struct inode* s_inode = d_inode(dentry);
-
- if (s_inode) {
- sr.volNumber = NCP_FINFO(s_inode)->volNumber;
- sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum;
- sr.namespace = server->name_space[sr.volNumber];
- result = 0;
- } else
- ncp_dbg(1, "d_inode(s_root)==NULL\n");
- } else
- ncp_dbg(1, "s_root==NULL\n");
- } else {
- sr.volNumber = -1;
- sr.namespace = 0;
- sr.dirEntNum = 0;
- result = 0;
- }
- mutex_unlock(&server->root_setup_lock);
- if (!result && copy_to_user(argp, &sr, sizeof(sr)))
- result = -EFAULT;
- return result;
- }
-
- case NCP_IOC_SETROOT:
- {
- struct ncp_setroot_ioctl sr;
- __u32 vnum;
- __le32 de;
- __le32 dosde;
- struct dentry* dentry;
-
- if (copy_from_user(&sr, argp, sizeof(sr)))
- return -EFAULT;
- mutex_lock(&server->root_setup_lock);
- if (server->root_setuped)
- result = -EBUSY;
- else {
- if (sr.volNumber < 0) {
- server->m.mounted_vol[0] = 0;
- vnum = NCP_NUMBER_OF_VOLUMES;
- de = 0;
- dosde = 0;
- result = 0;
- } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
- result = -EINVAL;
- } else if (ncp_mount_subdir(server, sr.volNumber,
- sr.namespace, sr.dirEntNum,
- &vnum, &de, &dosde)) {
- result = -ENOENT;
- } else
- result = 0;
-
- if (result == 0) {
- dentry = inode->i_sb->s_root;
- if (dentry) {
- struct inode* s_inode = d_inode(dentry);
-
- if (s_inode) {
- NCP_FINFO(s_inode)->volNumber = vnum;
- NCP_FINFO(s_inode)->dirEntNum = de;
- NCP_FINFO(s_inode)->DosDirNum = dosde;
- server->root_setuped = 1;
- } else {
- ncp_dbg(1, "d_inode(s_root)==NULL\n");
- result = -EIO;
- }
- } else {
- ncp_dbg(1, "s_root==NULL\n");
- result = -EIO;
- }
- }
- }
- mutex_unlock(&server->root_setup_lock);
-
- return result;
- }
-
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- case NCP_IOC_SIGN_INIT:
- {
- struct ncp_sign_init sign;
-
- if (argp)
- if (copy_from_user(&sign, argp, sizeof(sign)))
- return -EFAULT;
- ncp_lock_server(server);
- mutex_lock(&server->rcv.creq_mutex);
- if (argp) {
- if (server->sign_wanted) {
- memcpy(server->sign_root,sign.sign_root,8);
- memcpy(server->sign_last,sign.sign_last,16);
- server->sign_active = 1;
- }
- /* ignore when signatures not wanted */
- } else {
- server->sign_active = 0;
- }
- mutex_unlock(&server->rcv.creq_mutex);
- ncp_unlock_server(server);
- return 0;
- }
-
- case NCP_IOC_SIGN_WANTED:
- {
- int state;
-
- ncp_lock_server(server);
- state = server->sign_wanted;
- ncp_unlock_server(server);
- if (put_user(state, (int __user *)argp))
- return -EFAULT;
- return 0;
- }
-
- case NCP_IOC_SET_SIGN_WANTED:
- {
- int newstate;
-
- /* get only low 8 bits... */
- if (get_user(newstate, (unsigned char __user *)argp))
- return -EFAULT;
- result = 0;
- ncp_lock_server(server);
- if (server->sign_active) {
- /* cannot turn signatures OFF when active */
- if (!newstate)
- result = -EINVAL;
- } else {
- server->sign_wanted = newstate != 0;
- }
- ncp_unlock_server(server);
- return result;
- }
-
-#endif /* CONFIG_NCPFS_PACKET_SIGNING */
-
-#ifdef CONFIG_NCPFS_IOCTL_LOCKING
- case NCP_IOC_LOCKUNLOCK:
- {
- struct ncp_lock_ioctl rqdata;
-
- if (copy_from_user(&rqdata, argp, sizeof(rqdata)))
- return -EFAULT;
- if (rqdata.origin != 0)
- return -EINVAL;
- /* check for cmd */
- switch (rqdata.cmd) {
- case NCP_LOCK_EX:
- case NCP_LOCK_SH:
- if (rqdata.timeout < 0)
- return -EINVAL;
- if (rqdata.timeout == 0)
- rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
- else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
- rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
- break;
- case NCP_LOCK_LOG:
- rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
- case NCP_LOCK_CLEAR:
- break;
- default:
- return -EINVAL;
- }
- /* locking needs both read and write access */
- if ((result = ncp_make_open(inode, O_RDWR)) != 0)
- {
- return result;
- }
- result = -EISDIR;
- if (!S_ISREG(inode->i_mode))
- goto outrel;
- if (rqdata.cmd == NCP_LOCK_CLEAR)
- {
- result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
- NCP_FINFO(inode)->file_handle,
- rqdata.offset,
- rqdata.length);
- if (result > 0) result = 0; /* no such lock */
- }
- else
- {
- int lockcmd;
-
- switch (rqdata.cmd)
- {
- case NCP_LOCK_EX: lockcmd=1; break;
- case NCP_LOCK_SH: lockcmd=3; break;
- default: lockcmd=0; break;
- }
- result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
- NCP_FINFO(inode)->file_handle,
- lockcmd,
- rqdata.offset,
- rqdata.length,
- rqdata.timeout);
- if (result > 0) result = -EAGAIN;
- }
-outrel:
- ncp_inode_close(inode);
- return result;
- }
-#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
-
-#ifdef CONFIG_COMPAT
- case NCP_IOC_GETOBJECTNAME_32:
- {
- struct compat_ncp_objectname_ioctl user;
- size_t outl;
-
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
- down_read(&server->auth_rwsem);
- user.auth_type = server->auth.auth_type;
- outl = user.object_name_len;
- user.object_name_len = server->auth.object_name_len;
- if (outl > user.object_name_len)
- outl = user.object_name_len;
- result = 0;
- if (outl) {
- if (copy_to_user(compat_ptr(user.object_name),
- server->auth.object_name,
- outl))
- result = -EFAULT;
- }
- up_read(&server->auth_rwsem);
- if (!result && copy_to_user(argp, &user, sizeof(user)))
- result = -EFAULT;
- return result;
- }
-#endif
-
- case NCP_IOC_GETOBJECTNAME:
- {
- struct ncp_objectname_ioctl user;
- size_t outl;
-
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
- down_read(&server->auth_rwsem);
- user.auth_type = server->auth.auth_type;
- outl = user.object_name_len;
- user.object_name_len = server->auth.object_name_len;
- if (outl > user.object_name_len)
- outl = user.object_name_len;
- result = 0;
- if (outl) {
- if (copy_to_user(user.object_name,
- server->auth.object_name,
- outl))
- result = -EFAULT;
- }
- up_read(&server->auth_rwsem);
- if (!result && copy_to_user(argp, &user, sizeof(user)))
- result = -EFAULT;
- return result;
- }
-
-#ifdef CONFIG_COMPAT
- case NCP_IOC_SETOBJECTNAME_32:
-#endif
- case NCP_IOC_SETOBJECTNAME:
- {
- struct ncp_objectname_ioctl user;
- void* newname;
- void* oldname;
- size_t oldnamelen;
- void* oldprivate;
- size_t oldprivatelen;
-
-#ifdef CONFIG_COMPAT
- if (cmd == NCP_IOC_SETOBJECTNAME_32) {
- struct compat_ncp_objectname_ioctl user32;
- if (copy_from_user(&user32, argp, sizeof(user32)))
- return -EFAULT;
- user.auth_type = user32.auth_type;
- user.object_name_len = user32.object_name_len;
- user.object_name = compat_ptr(user32.object_name);
- } else
-#endif
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
-
- if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
- return -ENOMEM;
- if (user.object_name_len) {
- newname = memdup_user(user.object_name,
- user.object_name_len);
- if (IS_ERR(newname))
- return PTR_ERR(newname);
- } else {
- newname = NULL;
- }
- down_write(&server->auth_rwsem);
- oldname = server->auth.object_name;
- oldnamelen = server->auth.object_name_len;
- oldprivate = server->priv.data;
- oldprivatelen = server->priv.len;
- server->auth.auth_type = user.auth_type;
- server->auth.object_name_len = user.object_name_len;
- server->auth.object_name = newname;
- server->priv.len = 0;
- server->priv.data = NULL;
- up_write(&server->auth_rwsem);
- kfree(oldprivate);
- kfree(oldname);
- return 0;
- }
-
-#ifdef CONFIG_COMPAT
- case NCP_IOC_GETPRIVATEDATA_32:
-#endif
- case NCP_IOC_GETPRIVATEDATA:
- {
- struct ncp_privatedata_ioctl user;
- size_t outl;
-
-#ifdef CONFIG_COMPAT
- if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
- struct compat_ncp_privatedata_ioctl user32;
- if (copy_from_user(&user32, argp, sizeof(user32)))
- return -EFAULT;
- user.len = user32.len;
- user.data = compat_ptr(user32.data);
- } else
-#endif
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
-
- down_read(&server->auth_rwsem);
- outl = user.len;
- user.len = server->priv.len;
- if (outl > user.len) outl = user.len;
- result = 0;
- if (outl) {
- if (copy_to_user(user.data,
- server->priv.data,
- outl))
- result = -EFAULT;
- }
- up_read(&server->auth_rwsem);
- if (result)
- return result;
-#ifdef CONFIG_COMPAT
- if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
- struct compat_ncp_privatedata_ioctl user32;
- user32.len = user.len;
- user32.data = (unsigned long) user.data;
- if (copy_to_user(argp, &user32, sizeof(user32)))
- return -EFAULT;
- } else
-#endif
- if (copy_to_user(argp, &user, sizeof(user)))
- return -EFAULT;
-
- return 0;
- }
-
-#ifdef CONFIG_COMPAT
- case NCP_IOC_SETPRIVATEDATA_32:
-#endif
- case NCP_IOC_SETPRIVATEDATA:
- {
- struct ncp_privatedata_ioctl user;
- void* new;
- void* old;
- size_t oldlen;
-
-#ifdef CONFIG_COMPAT
- if (cmd == NCP_IOC_SETPRIVATEDATA_32) {
- struct compat_ncp_privatedata_ioctl user32;
- if (copy_from_user(&user32, argp, sizeof(user32)))
- return -EFAULT;
- user.len = user32.len;
- user.data = compat_ptr(user32.data);
- } else
-#endif
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
-
- if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
- return -ENOMEM;
- if (user.len) {
- new = memdup_user(user.data, user.len);
- if (IS_ERR(new))
- return PTR_ERR(new);
- } else {
- new = NULL;
- }
- down_write(&server->auth_rwsem);
- old = server->priv.data;
- oldlen = server->priv.len;
- server->priv.len = user.len;
- server->priv.data = new;
- up_write(&server->auth_rwsem);
- kfree(old);
- return 0;
- }
-
-#ifdef CONFIG_NCPFS_NLS
- case NCP_IOC_SETCHARSETS:
- return ncp_set_charsets(server, argp);
-
- case NCP_IOC_GETCHARSETS:
- return ncp_get_charsets(server, argp);
-
-#endif /* CONFIG_NCPFS_NLS */
-
- case NCP_IOC_SETDENTRYTTL:
- {
- u_int32_t user;
-
- if (copy_from_user(&user, argp, sizeof(user)))
- return -EFAULT;
- /* 20 secs at most... */
- if (user > 20000)
- return -EINVAL;
- user = (user * HZ) / 1000;
- atomic_set(&server->dentry_ttl, user);
- return 0;
- }
-
- case NCP_IOC_GETDENTRYTTL:
- {
- u_int32_t user = (atomic_read(&server->dentry_ttl) * 1000) / HZ;
- if (copy_to_user(argp, &user, sizeof(user)))
- return -EFAULT;
- return 0;
- }
-
- }
- return -EINVAL;
-}
-
-long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct inode *inode = file_inode(filp);
- struct ncp_server *server = NCP_SERVER(inode);
- kuid_t uid = current_uid();
- int need_drop_write = 0;
- long ret;
-
- switch (cmd) {
- case NCP_IOC_SETCHARSETS:
- case NCP_IOC_CONN_LOGGED_IN:
- case NCP_IOC_SETROOT:
- if (!capable(CAP_SYS_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- break;
- }
- if (!uid_eq(server->m.mounted_uid, uid)) {
- switch (cmd) {
- /*
- * Only mount owner can issue these ioctls. Information
- * necessary to authenticate to other NDS servers are
- * stored here.
- */
- case NCP_IOC_GETOBJECTNAME:
- case NCP_IOC_SETOBJECTNAME:
- case NCP_IOC_GETPRIVATEDATA:
- case NCP_IOC_SETPRIVATEDATA:
-#ifdef CONFIG_COMPAT
- case NCP_IOC_GETOBJECTNAME_32:
- case NCP_IOC_SETOBJECTNAME_32:
- case NCP_IOC_GETPRIVATEDATA_32:
- case NCP_IOC_SETPRIVATEDATA_32:
-#endif
- ret = -EACCES;
- goto out;
- /*
- * These require write access on the inode if user id
- * does not match. Note that they do not write to the
- * file... But old code did mnt_want_write, so I keep
- * it as is. Of course not for mountpoint owner, as
- * that breaks read-only mounts altogether as ncpmount
- * needs working NCP_IOC_NCPREQUEST and
- * NCP_IOC_GET_FS_INFO. Some of these codes (setdentryttl,
- * signinit, setsignwanted) should be probably restricted
- * to owner only, or even more to CAP_SYS_ADMIN).
- */
- case NCP_IOC_GET_FS_INFO:
- case NCP_IOC_GET_FS_INFO_V2:
- case NCP_IOC_NCPREQUEST:
- case NCP_IOC_SETDENTRYTTL:
- case NCP_IOC_SIGN_INIT:
- case NCP_IOC_LOCKUNLOCK:
- case NCP_IOC_SET_SIGN_WANTED:
-#ifdef CONFIG_COMPAT
- case NCP_IOC_GET_FS_INFO_V2_32:
- case NCP_IOC_NCPREQUEST_32:
-#endif
- ret = mnt_want_write_file(filp);
- if (ret)
- goto out;
- need_drop_write = 1;
- ret = inode_permission(inode, MAY_WRITE);
- if (ret)
- goto outDropWrite;
- break;
- /*
- * Read access required.
- */
- case NCP_IOC_GETMOUNTUID16:
- case NCP_IOC_GETMOUNTUID32:
- case NCP_IOC_GETMOUNTUID64:
- case NCP_IOC_GETROOT:
- case NCP_IOC_SIGN_WANTED:
- ret = inode_permission(inode, MAY_READ);
- if (ret)
- goto out;
- break;
- /*
- * Anybody can read these.
- */
- case NCP_IOC_GETCHARSETS:
- case NCP_IOC_GETDENTRYTTL:
- default:
- /* Three codes below are protected by CAP_SYS_ADMIN above. */
- case NCP_IOC_SETCHARSETS:
- case NCP_IOC_CONN_LOGGED_IN:
- case NCP_IOC_SETROOT:
- break;
- }
- }
- ret = __ncp_ioctl(inode, cmd, arg);
-outDropWrite:
- if (need_drop_write)
- mnt_drop_write_file(filp);
-out:
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- long ret;
-
- arg = (unsigned long) compat_ptr(arg);
- ret = ncp_ioctl(file, cmd, arg);
- return ret;
-}
-#endif
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
deleted file mode 100644
index a5c5cf2ff007..000000000000
--- a/fs/ncpfs/mmap.c
+++ /dev/null
@@ -1,125 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * mmap.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- *
- */
-
-#include <linux/stat.h>
-#include <linux/time.h>
-#include <linux/kernel.h>
-#include <linux/gfp.h>
-#include <linux/mm.h>
-#include <linux/shm.h>
-#include <linux/errno.h>
-#include <linux/mman.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/memcontrol.h>
-
-#include <linux/uaccess.h>
-
-#include "ncp_fs.h"
-
-/*
- * Fill in the supplied page for mmap
- * XXX: how are we excluding truncate/invalidate here? Maybe need to lock
- * page?
- */
-static int ncp_file_mmap_fault(struct vm_fault *vmf)
-{
- struct inode *inode = file_inode(vmf->vma->vm_file);
- char *pg_addr;
- unsigned int already_read;
- unsigned int count;
- int bufsize;
- int pos; /* XXX: loff_t ? */
-
- /*
- * ncpfs has nothing against high pages as long
- * as recvmsg and memset works on it
- */
- vmf->page = alloc_page(GFP_HIGHUSER);
- if (!vmf->page)
- return VM_FAULT_OOM;
- pg_addr = kmap(vmf->page);
- pos = vmf->pgoff << PAGE_SHIFT;
-
- count = PAGE_SIZE;
- /* what we can read in one go */
- bufsize = NCP_SERVER(inode)->buffer_size;
-
- already_read = 0;
- if (ncp_make_open(inode, O_RDONLY) >= 0) {
- while (already_read < count) {
- int read_this_time;
- int to_read;
-
- to_read = bufsize - (pos % bufsize);
-
- to_read = min_t(unsigned int, to_read, count - already_read);
-
- if (ncp_read_kernel(NCP_SERVER(inode),
- NCP_FINFO(inode)->file_handle,
- pos, to_read,
- pg_addr + already_read,
- &read_this_time) != 0) {
- read_this_time = 0;
- }
- pos += read_this_time;
- already_read += read_this_time;
-
- if (read_this_time < to_read) {
- break;
- }
- }
- ncp_inode_close(inode);
-
- }
-
- if (already_read < PAGE_SIZE)
- memset(pg_addr + already_read, 0, PAGE_SIZE - already_read);
- flush_dcache_page(vmf->page);
- kunmap(vmf->page);
-
- /*
- * If I understand ncp_read_kernel() properly, the above always
- * fetches from the network, here the analogue of disk.
- * -- nyc
- */
- count_vm_event(PGMAJFAULT);
- count_memcg_event_mm(vmf->vma->vm_mm, PGMAJFAULT);
- return VM_FAULT_MAJOR;
-}
-
-static const struct vm_operations_struct ncp_file_mmap =
-{
- .fault = ncp_file_mmap_fault,
-};
-
-
-/* This is used for a general mmap of a ncp file */
-int ncp_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct inode *inode = file_inode(file);
-
- ncp_dbg(1, "called\n");
-
- if (!ncp_conn_valid(NCP_SERVER(inode)))
- return -EIO;
-
- /* only PAGE_COW or read-only supported now */
- if (vma->vm_flags & VM_SHARED)
- return -EINVAL;
- /* we do not support files bigger than 4GB... We eventually
- supports just 4GB... */
- if (vma_pages(vma) + vma->vm_pgoff
- > (1U << (32 - PAGE_SHIFT)))
- return -EFBIG;
-
- vma->vm_ops = &ncp_file_mmap;
- file_accessed(file);
- return 0;
-}
diff --git a/fs/ncpfs/ncp_fs.h b/fs/ncpfs/ncp_fs.h
deleted file mode 100644
index bdd262b6c198..000000000000
--- a/fs/ncpfs/ncp_fs.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/ncp_fs.h>
-#include "ncp_fs_i.h"
-#include "ncp_fs_sb.h"
-
-#undef NCPFS_PARANOIA
-#ifdef NCPFS_PARANOIA
-#define ncp_vdbg(fmt, ...) \
- pr_debug(fmt, ##__VA_ARGS__)
-#else
-#define ncp_vdbg(fmt, ...) \
-do { \
- if (0) \
- pr_debug(fmt, ##__VA_ARGS__); \
-} while (0)
-#endif
-
-#ifndef DEBUG_NCP
-#define DEBUG_NCP 0
-#endif
-
-#if DEBUG_NCP > 0 && !defined(DEBUG)
-#define DEBUG
-#endif
-
-#define ncp_dbg(level, fmt, ...) \
-do { \
- if (level <= DEBUG_NCP) \
- pr_debug(fmt, ##__VA_ARGS__); \
-} while (0)
-
-#define NCP_MAX_RPC_TIMEOUT (6*HZ)
-
-
-struct ncp_entry_info {
- struct nw_info_struct i;
- ino_t ino;
- int opened;
- int access;
- unsigned int volume;
- __u8 file_handle[6];
-};
-
-static inline struct ncp_server *NCP_SBP(const struct super_block *sb)
-{
- return sb->s_fs_info;
-}
-
-#define NCP_SERVER(inode) NCP_SBP((inode)->i_sb)
-static inline struct ncp_inode_info *NCP_FINFO(const struct inode *inode)
-{
- return container_of(inode, struct ncp_inode_info, vfs_inode);
-}
-
-/* linux/fs/ncpfs/inode.c */
-int ncp_notify_change(struct dentry *, struct iattr *);
-struct inode *ncp_iget(struct super_block *, struct ncp_entry_info *);
-void ncp_update_inode(struct inode *, struct ncp_entry_info *);
-void ncp_update_inode2(struct inode *, struct ncp_entry_info *);
-
-/* linux/fs/ncpfs/dir.c */
-extern const struct inode_operations ncp_dir_inode_operations;
-extern const struct file_operations ncp_dir_operations;
-extern const struct dentry_operations ncp_dentry_operations;
-int ncp_conn_logged_in(struct super_block *);
-int ncp_date_dos2unix(__le16 time, __le16 date);
-void ncp_date_unix2dos(int unix_date, __le16 * time, __le16 * date);
-
-/* linux/fs/ncpfs/ioctl.c */
-long ncp_ioctl(struct file *, unsigned int, unsigned long);
-long ncp_compat_ioctl(struct file *, unsigned int, unsigned long);
-
-/* linux/fs/ncpfs/sock.c */
-int ncp_request2(struct ncp_server *server, int function,
- void* reply, int max_reply_size);
-static inline int ncp_request(struct ncp_server *server, int function) {
- return ncp_request2(server, function, server->packet, server->packet_size);
-}
-int ncp_connect(struct ncp_server *server);
-int ncp_disconnect(struct ncp_server *server);
-void ncp_lock_server(struct ncp_server *server);
-void ncp_unlock_server(struct ncp_server *server);
-
-/* linux/fs/ncpfs/symlink.c */
-#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
-extern const struct address_space_operations ncp_symlink_aops;
-int ncp_symlink(struct inode*, struct dentry*, const char*);
-#endif
-
-/* linux/fs/ncpfs/file.c */
-extern const struct inode_operations ncp_file_inode_operations;
-extern const struct file_operations ncp_file_operations;
-int ncp_make_open(struct inode *, int);
-
-/* linux/fs/ncpfs/mmap.c */
-int ncp_mmap(struct file *, struct vm_area_struct *);
-
-/* linux/fs/ncpfs/ncplib_kernel.c */
-int ncp_make_closed(struct inode *);
-
-#include "ncplib_kernel.h"
diff --git a/fs/ncpfs/ncp_fs_i.h b/fs/ncpfs/ncp_fs_i.h
deleted file mode 100644
index 3432bafb53a5..000000000000
--- a/fs/ncpfs/ncp_fs_i.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ncp_fs_i.h
- *
- * Copyright (C) 1995 Volker Lendecke
- *
- */
-
-#ifndef _LINUX_NCP_FS_I
-#define _LINUX_NCP_FS_I
-
-/*
- * This is the ncpfs part of the inode structure. This must contain
- * all the information we need to work with an inode after creation.
- */
-struct ncp_inode_info {
- __le32 dirEntNum;
- __le32 DosDirNum;
- __u8 volNumber;
- __le32 nwattr;
- struct mutex open_mutex;
- atomic_t opened;
- int access;
- int flags;
-#define NCPI_KLUDGE_SYMLINK 0x0001
-#define NCPI_DIR_CACHE 0x0002
- __u8 file_handle[6];
- struct inode vfs_inode;
-};
-
-#endif /* _LINUX_NCP_FS_I */
diff --git a/fs/ncpfs/ncp_fs_sb.h b/fs/ncpfs/ncp_fs_sb.h
deleted file mode 100644
index f06cde4adf71..000000000000
--- a/fs/ncpfs/ncp_fs_sb.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ncp_fs_sb.h
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- *
- */
-
-#ifndef _NCP_FS_SB
-#define _NCP_FS_SB
-
-#include <linux/types.h>
-#include <linux/ncp_mount.h>
-#include <linux/net.h>
-#include <linux/mutex.h>
-#include <linux/backing-dev.h>
-#include <linux/workqueue.h>
-
-#define NCP_DEFAULT_OPTIONS 0 /* 2 for packet signatures */
-
-struct sock;
-
-struct ncp_mount_data_kernel {
- unsigned long flags; /* NCP_MOUNT_* flags */
- unsigned int int_flags; /* internal flags */
-#define NCP_IMOUNT_LOGGEDIN_POSSIBLE 0x0001
- kuid_t mounted_uid; /* Who may umount() this filesystem? */
- struct pid *wdog_pid; /* Who cares for our watchdog packets? */
- unsigned int ncp_fd; /* The socket to the ncp port */
- unsigned int time_out; /* How long should I wait after
- sending a NCP request? */
- unsigned int retry_count; /* And how often should I retry? */
- unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
- kuid_t uid;
- kgid_t gid;
- umode_t file_mode;
- umode_t dir_mode;
- int info_fd;
-};
-
-struct ncp_server {
- struct rcu_head rcu;
- struct ncp_mount_data_kernel m; /* Nearly all of the mount data is of
- interest for us later, so we store
- it completely. */
-
- __u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
-
- struct socket *ncp_sock;/* ncp socket */
- struct socket *info_sock;
-
- u8 sequence;
- u8 task;
- u16 connection; /* Remote connection number */
-
- u8 completion; /* Status message from server */
- u8 conn_status; /* Bit 4 = 1 ==> Server going down, no
- requests allowed anymore.
- Bit 0 = 1 ==> Server is down. */
-
- int buffer_size; /* Negotiated bufsize */
-
- int reply_size; /* Size of last reply */
-
- int packet_size;
- unsigned char *packet; /* Here we prepare requests and
- receive replies */
- unsigned char *txbuf; /* Storage for current request */
- unsigned char *rxbuf; /* Storage for reply to current request */
-
- int lock; /* To prevent mismatch in protocols. */
- struct mutex mutex;
-
- int current_size; /* for packet preparation */
- int has_subfunction;
- int ncp_reply_size;
-
- int root_setuped;
- struct mutex root_setup_lock;
-
- /* info for packet signing */
- int sign_wanted; /* 1=Server needs signed packets */
- int sign_active; /* 0=don't do signing, 1=do */
- char sign_root[8]; /* generated from password and encr. key */
- char sign_last[16];
-
- /* Authentication info: NDS or BINDERY, username */
- struct {
- int auth_type;
- size_t object_name_len;
- void* object_name;
- int object_type;
- } auth;
- /* Password info */
- struct {
- size_t len;
- void* data;
- } priv;
- struct rw_semaphore auth_rwsem;
-
- /* nls info: codepage for volume and charset for I/O */
- struct nls_table *nls_vol;
- struct nls_table *nls_io;
-
- /* maximum age in jiffies */
- atomic_t dentry_ttl;
-
- /* miscellaneous */
- unsigned int flags;
-
- spinlock_t requests_lock; /* Lock accesses to tx.requests, tx.creq and rcv.creq when STREAM mode */
-
- void (*data_ready)(struct sock* sk);
- void (*error_report)(struct sock* sk);
- void (*write_space)(struct sock* sk); /* STREAM mode only */
- struct {
- struct work_struct tq; /* STREAM/DGRAM: data/error ready */
- struct ncp_request_reply* creq; /* STREAM/DGRAM: awaiting reply from this request */
- struct mutex creq_mutex; /* DGRAM only: lock accesses to rcv.creq */
-
- unsigned int state; /* STREAM only: receiver state */
- struct {
- __u32 magic __packed;
- __u32 len __packed;
- __u16 type __packed;
- __u16 p1 __packed;
- __u16 p2 __packed;
- __u16 p3 __packed;
- __u16 type2 __packed;
- } buf; /* STREAM only: temporary buffer */
- unsigned char* ptr; /* STREAM only: pointer to data */
- size_t len; /* STREAM only: length of data to receive */
- } rcv;
- struct {
- struct list_head requests; /* STREAM only: queued requests */
- struct work_struct tq; /* STREAM only: transmitter ready */
- struct ncp_request_reply* creq; /* STREAM only: currently transmitted entry */
- } tx;
- struct timer_list timeout_tm; /* DGRAM only: timeout timer */
- struct work_struct timeout_tq; /* DGRAM only: associated queue, we run timers from process context */
- int timeout_last; /* DGRAM only: current timeout length */
- int timeout_retries; /* DGRAM only: retries left */
- struct {
- size_t len;
- __u8 data[128];
- } unexpected_packet;
-};
-
-extern void ncp_tcp_rcv_proc(struct work_struct *work);
-extern void ncp_tcp_tx_proc(struct work_struct *work);
-extern void ncpdgram_rcv_proc(struct work_struct *work);
-extern void ncpdgram_timeout_proc(struct work_struct *work);
-extern void ncpdgram_timeout_call(struct timer_list *t);
-extern void ncp_tcp_data_ready(struct sock* sk);
-extern void ncp_tcp_write_space(struct sock* sk);
-extern void ncp_tcp_error_report(struct sock* sk);
-
-#define NCP_FLAG_UTF8 1
-
-#define NCP_CLR_FLAG(server, flag) ((server)->flags &= ~(flag))
-#define NCP_SET_FLAG(server, flag) ((server)->flags |= (flag))
-#define NCP_IS_FLAG(server, flag) ((server)->flags & (flag))
-
-static inline int ncp_conn_valid(struct ncp_server *server)
-{
- return ((server->conn_status & 0x11) == 0);
-}
-
-static inline void ncp_invalidate_conn(struct ncp_server *server)
-{
- server->conn_status |= 0x01;
-}
-
-#endif
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
deleted file mode 100644
index 804adfebba2f..000000000000
--- a/fs/ncpfs/ncplib_kernel.c
+++ /dev/null
@@ -1,1322 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ncplib_kernel.c
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified for big endian by J.F. Chadima and David S. Miller
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- * Modified 1999 Wolfram Pienkoss for NLS
- * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "ncp_fs.h"
-
-static inline void assert_server_locked(struct ncp_server *server)
-{
- if (server->lock == 0) {
- ncp_dbg(1, "server not locked!\n");
- }
-}
-
-static void ncp_add_byte(struct ncp_server *server, __u8 x)
-{
- assert_server_locked(server);
- *(__u8 *) (&(server->packet[server->current_size])) = x;
- server->current_size += 1;
- return;
-}
-
-static void ncp_add_word(struct ncp_server *server, __le16 x)
-{
- assert_server_locked(server);
- put_unaligned(x, (__le16 *) (&(server->packet[server->current_size])));
- server->current_size += 2;
- return;
-}
-
-static void ncp_add_be16(struct ncp_server *server, __u16 x)
-{
- assert_server_locked(server);
- put_unaligned(cpu_to_be16(x), (__be16 *) (&(server->packet[server->current_size])));
- server->current_size += 2;
-}
-
-static void ncp_add_dword(struct ncp_server *server, __le32 x)
-{
- assert_server_locked(server);
- put_unaligned(x, (__le32 *) (&(server->packet[server->current_size])));
- server->current_size += 4;
- return;
-}
-
-static void ncp_add_be32(struct ncp_server *server, __u32 x)
-{
- assert_server_locked(server);
- put_unaligned(cpu_to_be32(x), (__be32 *)(&(server->packet[server->current_size])));
- server->current_size += 4;
-}
-
-static inline void ncp_add_dword_lh(struct ncp_server *server, __u32 x) {
- ncp_add_dword(server, cpu_to_le32(x));
-}
-
-static void ncp_add_mem(struct ncp_server *server, const void *source, int size)
-{
- assert_server_locked(server);
- memcpy(&(server->packet[server->current_size]), source, size);
- server->current_size += size;
- return;
-}
-
-static void ncp_add_pstring(struct ncp_server *server, const char *s)
-{
- int len = strlen(s);
- assert_server_locked(server);
- if (len > 255) {
- ncp_dbg(1, "string too long: %s\n", s);
- len = 255;
- }
- ncp_add_byte(server, len);
- ncp_add_mem(server, s, len);
- return;
-}
-
-static inline void ncp_init_request(struct ncp_server *server)
-{
- ncp_lock_server(server);
-
- server->current_size = sizeof(struct ncp_request_header);
- server->has_subfunction = 0;
-}
-
-static inline void ncp_init_request_s(struct ncp_server *server, int subfunction)
-{
- ncp_lock_server(server);
-
- server->current_size = sizeof(struct ncp_request_header) + 2;
- ncp_add_byte(server, subfunction);
-
- server->has_subfunction = 1;
-}
-
-static inline char *
-ncp_reply_data(struct ncp_server *server, int offset)
-{
- return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
-}
-
-static inline u8 BVAL(const void *data)
-{
- return *(const u8 *)data;
-}
-
-static u8 ncp_reply_byte(struct ncp_server *server, int offset)
-{
- return *(const u8 *)ncp_reply_data(server, offset);
-}
-
-static inline u16 WVAL_LH(const void *data)
-{
- return get_unaligned_le16(data);
-}
-
-static u16
-ncp_reply_le16(struct ncp_server *server, int offset)
-{
- return get_unaligned_le16(ncp_reply_data(server, offset));
-}
-
-static u16
-ncp_reply_be16(struct ncp_server *server, int offset)
-{
- return get_unaligned_be16(ncp_reply_data(server, offset));
-}
-
-static inline u32 DVAL_LH(const void *data)
-{
- return get_unaligned_le32(data);
-}
-
-static __le32
-ncp_reply_dword(struct ncp_server *server, int offset)
-{
- return get_unaligned((__le32 *)ncp_reply_data(server, offset));
-}
-
-static inline __u32 ncp_reply_dword_lh(struct ncp_server* server, int offset) {
- return le32_to_cpu(ncp_reply_dword(server, offset));
-}
-
-int
-ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_be16(server, size);
-
- if ((result = ncp_request(server, 33)) != 0) {
- ncp_unlock_server(server);
- return result;
- }
- *target = min_t(unsigned int, ncp_reply_be16(server, 0), size);
-
- ncp_unlock_server(server);
- return 0;
-}
-
-
-/* options:
- * bit 0 ipx checksum
- * bit 1 packet signing
- */
-int
-ncp_negotiate_size_and_options(struct ncp_server *server,
- int size, int options, int *ret_size, int *ret_options) {
- int result;
-
- /* there is minimum */
- if (size < NCP_BLOCK_SIZE) size = NCP_BLOCK_SIZE;
-
- ncp_init_request(server);
- ncp_add_be16(server, size);
- ncp_add_byte(server, options);
-
- if ((result = ncp_request(server, 0x61)) != 0)
- {
- ncp_unlock_server(server);
- return result;
- }
-
- /* NCP over UDP returns 0 (!!!) */
- result = ncp_reply_be16(server, 0);
- if (result >= NCP_BLOCK_SIZE)
- size = min(result, size);
- *ret_size = size;
- *ret_options = ncp_reply_byte(server, 4);
-
- ncp_unlock_server(server);
- return 0;
-}
-
-int ncp_get_volume_info_with_number(struct ncp_server* server,
- int n, struct ncp_volume_info* target) {
- int result;
- int len;
-
- ncp_init_request_s(server, 44);
- ncp_add_byte(server, n);
-
- if ((result = ncp_request(server, 22)) != 0) {
- goto out;
- }
- target->total_blocks = ncp_reply_dword_lh(server, 0);
- target->free_blocks = ncp_reply_dword_lh(server, 4);
- target->purgeable_blocks = ncp_reply_dword_lh(server, 8);
- target->not_yet_purgeable_blocks = ncp_reply_dword_lh(server, 12);
- target->total_dir_entries = ncp_reply_dword_lh(server, 16);
- target->available_dir_entries = ncp_reply_dword_lh(server, 20);
- target->sectors_per_block = ncp_reply_byte(server, 28);
-
- memset(&(target->volume_name), 0, sizeof(target->volume_name));
-
- result = -EIO;
- len = ncp_reply_byte(server, 29);
- if (len > NCP_VOLNAME_LEN) {
- ncp_dbg(1, "volume name too long: %d\n", len);
- goto out;
- }
- memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
- result = 0;
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-int ncp_get_directory_info(struct ncp_server* server, __u8 n,
- struct ncp_volume_info* target) {
- int result;
- int len;
-
- ncp_init_request_s(server, 45);
- ncp_add_byte(server, n);
-
- if ((result = ncp_request(server, 22)) != 0) {
- goto out;
- }
- target->total_blocks = ncp_reply_dword_lh(server, 0);
- target->free_blocks = ncp_reply_dword_lh(server, 4);
- target->purgeable_blocks = 0;
- target->not_yet_purgeable_blocks = 0;
- target->total_dir_entries = ncp_reply_dword_lh(server, 8);
- target->available_dir_entries = ncp_reply_dword_lh(server, 12);
- target->sectors_per_block = ncp_reply_byte(server, 20);
-
- memset(&(target->volume_name), 0, sizeof(target->volume_name));
-
- result = -EIO;
- len = ncp_reply_byte(server, 21);
- if (len > NCP_VOLNAME_LEN) {
- ncp_dbg(1, "volume name too long: %d\n", len);
- goto out;
- }
- memcpy(&(target->volume_name), ncp_reply_data(server, 22), len);
- result = 0;
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-int
-ncp_close_file(struct ncp_server *server, const char *file_id)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 0);
- ncp_add_mem(server, file_id, 6);
-
- result = ncp_request(server, 66);
- ncp_unlock_server(server);
- return result;
-}
-
-int
-ncp_make_closed(struct inode *inode)
-{
- int err;
-
- err = 0;
- mutex_lock(&NCP_FINFO(inode)->open_mutex);
- if (atomic_read(&NCP_FINFO(inode)->opened) == 1) {
- atomic_set(&NCP_FINFO(inode)->opened, 0);
- err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle);
-
- if (!err)
- ncp_vdbg("volnum=%d, dirent=%u, error=%d\n",
- NCP_FINFO(inode)->volNumber,
- NCP_FINFO(inode)->dirEntNum, err);
- }
- mutex_unlock(&NCP_FINFO(inode)->open_mutex);
- return err;
-}
-
-static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
- __le32 dir_base, int have_dir_base,
- const char *path)
-{
- ncp_add_byte(server, vol_num);
- ncp_add_dword(server, dir_base);
- if (have_dir_base != 0) {
- ncp_add_byte(server, 1); /* dir_base */
- } else {
- ncp_add_byte(server, 0xff); /* no handle */
- }
- if (path != NULL) {
- ncp_add_byte(server, 1); /* 1 component */
- ncp_add_pstring(server, path);
- } else {
- ncp_add_byte(server, 0);
- }
-}
-
-int ncp_dirhandle_alloc(struct ncp_server* server, __u8 volnum, __le32 dirent,
- __u8* dirhandle) {
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 12); /* subfunction */
- ncp_add_byte(server, NW_NS_DOS);
- ncp_add_byte(server, 0);
- ncp_add_word(server, 0);
- ncp_add_handle_path(server, volnum, dirent, 1, NULL);
- if ((result = ncp_request(server, 87)) == 0) {
- *dirhandle = ncp_reply_byte(server, 0);
- }
- ncp_unlock_server(server);
- return result;
-}
-
-int ncp_dirhandle_free(struct ncp_server* server, __u8 dirhandle) {
- int result;
-
- ncp_init_request_s(server, 20);
- ncp_add_byte(server, dirhandle);
- result = ncp_request(server, 22);
- ncp_unlock_server(server);
- return result;
-}
-
-void ncp_extract_file_info(const void *structure, struct nw_info_struct *target)
-{
- const __u8 *name_len;
- const int info_struct_size = offsetof(struct nw_info_struct, nameLen);
-
- memcpy(target, structure, info_struct_size);
- name_len = structure + info_struct_size;
- target->nameLen = *name_len;
- memcpy(target->entryName, name_len + 1, *name_len);
- target->entryName[*name_len] = '\0';
- target->volNumber = le32_to_cpu(target->volNumber);
- return;
-}
-
-#ifdef CONFIG_NCPFS_NFS_NS
-static inline void ncp_extract_nfs_info(const unsigned char *structure,
- struct nw_nfs_info *target)
-{
- target->mode = DVAL_LH(structure);
- target->rdev = DVAL_LH(structure + 8);
-}
-#endif
-
-int ncp_obtain_nfs_info(struct ncp_server *server,
- struct nw_info_struct *target)
-
-{
- int result = 0;
-#ifdef CONFIG_NCPFS_NFS_NS
- __u32 volnum = target->volNumber;
-
- if (ncp_is_nfs_extras(server, volnum)) {
- ncp_init_request(server);
- ncp_add_byte(server, 19); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, NW_NS_NFS);
- ncp_add_byte(server, 0);
- ncp_add_byte(server, volnum);
- ncp_add_dword(server, target->dirEntNum);
- /* We must retrieve both nlinks and rdev, otherwise some server versions
- report zeroes instead of valid data */
- ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV);
-
- if ((result = ncp_request(server, 87)) == 0) {
- ncp_extract_nfs_info(ncp_reply_data(server, 0), &target->nfs);
- ncp_dbg(1, "(%s) mode=0%o, rdev=0x%x\n",
- target->entryName, target->nfs.mode,
- target->nfs.rdev);
- } else {
- target->nfs.mode = 0;
- target->nfs.rdev = 0;
- }
- ncp_unlock_server(server);
-
- } else
-#endif
- {
- target->nfs.mode = 0;
- target->nfs.rdev = 0;
- }
- return result;
-}
-
-/*
- * Returns information for a (one-component) name relative to
- * the specified directory.
- */
-int ncp_obtain_info(struct ncp_server *server, struct inode *dir, const char *path,
- struct nw_info_struct *target)
-{
- __u8 volnum = NCP_FINFO(dir)->volNumber;
- __le32 dirent = NCP_FINFO(dir)->dirEntNum;
- int result;
-
- if (target == NULL) {
- pr_err("%s: invalid call\n", __func__);
- return -EINVAL;
- }
- ncp_init_request(server);
- ncp_add_byte(server, 6); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
- ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
- ncp_add_dword(server, RIM_ALL);
- ncp_add_handle_path(server, volnum, dirent, 1, path);
-
- if ((result = ncp_request(server, 87)) != 0)
- goto out;
- ncp_extract_file_info(ncp_reply_data(server, 0), target);
- ncp_unlock_server(server);
-
- result = ncp_obtain_nfs_info(server, target);
- return result;
-
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-#ifdef CONFIG_NCPFS_NFS_NS
-static int
-ncp_obtain_DOS_dir_base(struct ncp_server *server,
- __u8 ns, __u8 volnum, __le32 dirent,
- const char *path, /* At most 1 component */
- __le32 *DOS_dir_base)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 6); /* subfunction */
- ncp_add_byte(server, ns);
- ncp_add_byte(server, ns);
- ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
- ncp_add_dword(server, RIM_DIRECTORY);
- ncp_add_handle_path(server, volnum, dirent, 1, path);
-
- if ((result = ncp_request(server, 87)) == 0)
- {
- if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
- }
- ncp_unlock_server(server);
- return result;
-}
-#endif /* CONFIG_NCPFS_NFS_NS */
-
-static inline int
-ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
-{
-#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
- int result;
- __u8 *namespace;
- __u16 no_namespaces;
-
- ncp_init_request(server);
- ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */
- ncp_add_word(server, 0);
- ncp_add_byte(server, volume);
-
- if ((result = ncp_request(server, 87)) != 0) {
- ncp_unlock_server(server);
- return NW_NS_DOS; /* not result ?? */
- }
-
- result = NW_NS_DOS;
- no_namespaces = ncp_reply_le16(server, 0);
- namespace = ncp_reply_data(server, 2);
-
- while (no_namespaces > 0) {
- ncp_dbg(1, "found %d on %d\n", *namespace, volume);
-
-#ifdef CONFIG_NCPFS_NFS_NS
- if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS))
- {
- result = NW_NS_NFS;
- break;
- }
-#endif /* CONFIG_NCPFS_NFS_NS */
-#ifdef CONFIG_NCPFS_OS2_NS
- if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
- {
- result = NW_NS_OS2;
- }
-#endif /* CONFIG_NCPFS_OS2_NS */
- namespace += 1;
- no_namespaces -= 1;
- }
- ncp_unlock_server(server);
- return result;
-#else /* neither OS2 nor NFS - only DOS */
- return NW_NS_DOS;
-#endif /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
-}
-
-int
-ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns)
-{
- int ns = ncp_get_known_namespace(server, volume);
-
- if (ret_ns)
- *ret_ns = ns;
-
- ncp_dbg(1, "namespace[%d] = %d\n", volume, server->name_space[volume]);
-
- if (server->name_space[volume] == ns)
- return 0;
- server->name_space[volume] = ns;
- return 1;
-}
-
-static int
-ncp_ObtainSpecificDirBase(struct ncp_server *server,
- __u8 nsSrc, __u8 nsDst, __u8 vol_num, __le32 dir_base,
- const char *path, /* At most 1 component */
- __le32 *dirEntNum, __le32 *DosDirNum)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 6); /* subfunction */
- ncp_add_byte(server, nsSrc);
- ncp_add_byte(server, nsDst);
- ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
- ncp_add_dword(server, RIM_ALL);
- ncp_add_handle_path(server, vol_num, dir_base, 1, path);
-
- if ((result = ncp_request(server, 87)) != 0)
- {
- ncp_unlock_server(server);
- return result;
- }
-
- if (dirEntNum)
- *dirEntNum = ncp_reply_dword(server, 0x30);
- if (DosDirNum)
- *DosDirNum = ncp_reply_dword(server, 0x34);
- ncp_unlock_server(server);
- return 0;
-}
-
-int
-ncp_mount_subdir(struct ncp_server *server,
- __u8 volNumber, __u8 srcNS, __le32 dirEntNum,
- __u32* volume, __le32* newDirEnt, __le32* newDosEnt)
-{
- int dstNS;
- int result;
-
- ncp_update_known_namespace(server, volNumber, &dstNS);
- if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber,
- dirEntNum, NULL, newDirEnt, newDosEnt)) != 0)
- {
- return result;
- }
- *volume = volNumber;
- server->m.mounted_vol[1] = 0;
- server->m.mounted_vol[0] = 'X';
- return 0;
-}
-
-int
-ncp_get_volume_root(struct ncp_server *server,
- const char *volname, __u32* volume, __le32* dirent, __le32* dosdirent)
-{
- int result;
-
- ncp_dbg(1, "looking up vol %s\n", volname);
-
- ncp_init_request(server);
- ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */
- ncp_add_byte(server, 0); /* DOS namespace */
- ncp_add_byte(server, 0); /* reserved */
- ncp_add_byte(server, 0); /* reserved */
- ncp_add_byte(server, 0); /* reserved */
-
- ncp_add_byte(server, 0); /* faked volume number */
- ncp_add_dword(server, 0); /* faked dir_base */
- ncp_add_byte(server, 0xff); /* Don't have a dir_base */
- ncp_add_byte(server, 1); /* 1 path component */
- ncp_add_pstring(server, volname);
-
- if ((result = ncp_request(server, 87)) != 0) {
- ncp_unlock_server(server);
- return result;
- }
- *dirent = *dosdirent = ncp_reply_dword(server, 4);
- *volume = ncp_reply_byte(server, 8);
- ncp_unlock_server(server);
- return 0;
-}
-
-int
-ncp_lookup_volume(struct ncp_server *server,
- const char *volname, struct nw_info_struct *target)
-{
- int result;
-
- memset(target, 0, sizeof(*target));
- result = ncp_get_volume_root(server, volname,
- &target->volNumber, &target->dirEntNum, &target->DosDirNum);
- if (result) {
- return result;
- }
- ncp_update_known_namespace(server, target->volNumber, NULL);
- target->nameLen = strlen(volname);
- memcpy(target->entryName, volname, target->nameLen+1);
- target->attributes = aDIR;
- /* set dates to Jan 1, 1986 00:00 */
- target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
- target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
- target->nfs.mode = 0;
- return 0;
-}
-
-int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
- struct inode *dir,
- const char *path,
- __le32 info_mask,
- const struct nw_modify_dos_info *info)
-{
- __u8 volnum = NCP_FINFO(dir)->volNumber;
- __le32 dirent = NCP_FINFO(dir)->dirEntNum;
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 7); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, 0); /* reserved */
- ncp_add_word(server, cpu_to_le16(0x8006)); /* search attribs: all */
-
- ncp_add_dword(server, info_mask);
- ncp_add_mem(server, info, sizeof(*info));
- ncp_add_handle_path(server, volnum, dirent, 1, path);
-
- result = ncp_request(server, 87);
- ncp_unlock_server(server);
- return result;
-}
-
-int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
- struct inode *dir,
- __le32 info_mask,
- const struct nw_modify_dos_info *info)
-{
- return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
- info_mask, info);
-}
-
-#ifdef CONFIG_NCPFS_NFS_NS
-int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent,
- __u32 mode, __u32 rdev)
-
-{
- int result = 0;
-
- ncp_init_request(server);
- if (server->name_space[volnum] == NW_NS_NFS) {
- ncp_add_byte(server, 25); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, NW_NS_NFS);
- ncp_add_byte(server, volnum);
- ncp_add_dword(server, dirent);
- /* we must always operate on both nlinks and rdev, otherwise
- rdev is not set */
- ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV);
- ncp_add_dword_lh(server, mode);
- ncp_add_dword_lh(server, 1); /* nlinks */
- ncp_add_dword_lh(server, rdev);
- result = ncp_request(server, 87);
- }
- ncp_unlock_server(server);
- return result;
-}
-#endif
-
-
-static int
-ncp_DeleteNSEntry(struct ncp_server *server,
- __u8 have_dir_base, __u8 volnum, __le32 dirent,
- const char* name, __u8 ns, __le16 attr)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 8); /* subfunction */
- ncp_add_byte(server, ns);
- ncp_add_byte(server, 0); /* reserved */
- ncp_add_word(server, attr); /* search attribs: all */
- ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
-
- result = ncp_request(server, 87);
- ncp_unlock_server(server);
- return result;
-}
-
-int
-ncp_del_file_or_subdir2(struct ncp_server *server,
- struct dentry *dentry)
-{
- struct inode *inode = d_inode(dentry);
- __u8 volnum;
- __le32 dirent;
-
- if (!inode) {
- return 0xFF; /* Any error */
- }
- volnum = NCP_FINFO(inode)->volNumber;
- dirent = NCP_FINFO(inode)->DosDirNum;
- return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006));
-}
-
-int
-ncp_del_file_or_subdir(struct ncp_server *server,
- struct inode *dir, const char *name)
-{
- __u8 volnum = NCP_FINFO(dir)->volNumber;
- __le32 dirent = NCP_FINFO(dir)->dirEntNum;
- int name_space;
-
- name_space = server->name_space[volnum];
-#ifdef CONFIG_NCPFS_NFS_NS
- if (name_space == NW_NS_NFS)
- {
- int result;
-
- result=ncp_obtain_DOS_dir_base(server, name_space, volnum, dirent, name, &dirent);
- if (result) return result;
- name = NULL;
- name_space = NW_NS_DOS;
- }
-#endif /* CONFIG_NCPFS_NFS_NS */
- return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, name_space, cpu_to_le16(0x8006));
-}
-
-static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6])
-{
- __le16 *dest = (__le16 *) ret;
- dest[1] = cpu_to_le16(v0);
- dest[2] = cpu_to_le16(v1);
- dest[0] = cpu_to_le16(v0 + 1);
- return;
-}
-
-/* If both dir and name are NULL, then in target there's already a
- looked-up entry that wants to be opened. */
-int ncp_open_create_file_or_subdir(struct ncp_server *server,
- struct inode *dir, const char *name,
- int open_create_mode,
- __le32 create_attributes,
- __le16 desired_acc_rights,
- struct ncp_entry_info *target)
-{
- __le16 search_attribs = cpu_to_le16(0x0006);
- __u8 volnum;
- __le32 dirent;
- int result;
-
- volnum = NCP_FINFO(dir)->volNumber;
- dirent = NCP_FINFO(dir)->dirEntNum;
-
- if ((create_attributes & aDIR) != 0) {
- search_attribs |= cpu_to_le16(0x8000);
- }
- ncp_init_request(server);
- ncp_add_byte(server, 1); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, open_create_mode);
- ncp_add_word(server, search_attribs);
- ncp_add_dword(server, RIM_ALL);
- ncp_add_dword(server, create_attributes);
- /* The desired acc rights seem to be the inherited rights mask
- for directories */
- ncp_add_word(server, desired_acc_rights);
- ncp_add_handle_path(server, volnum, dirent, 1, name);
-
- if ((result = ncp_request(server, 87)) != 0)
- goto out;
- if (!(create_attributes & aDIR))
- target->opened = 1;
-
- /* in target there's a new finfo to fill */
- ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
- target->volume = target->i.volNumber;
- ConvertToNWfromDWORD(ncp_reply_le16(server, 0),
- ncp_reply_le16(server, 2),
- target->file_handle);
-
- ncp_unlock_server(server);
-
- (void)ncp_obtain_nfs_info(server, &(target->i));
- return 0;
-
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-int
-ncp_initialize_search(struct ncp_server *server, struct inode *dir,
- struct nw_search_sequence *target)
-{
- __u8 volnum = NCP_FINFO(dir)->volNumber;
- __le32 dirent = NCP_FINFO(dir)->dirEntNum;
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 2); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
- ncp_add_byte(server, 0); /* reserved */
- ncp_add_handle_path(server, volnum, dirent, 1, NULL);
-
- result = ncp_request(server, 87);
- if (result)
- goto out;
- memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
-
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-int ncp_search_for_fileset(struct ncp_server *server,
- struct nw_search_sequence *seq,
- int* more,
- int* cnt,
- char* buffer,
- size_t bufsize,
- char** rbuf,
- size_t* rsize)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 20);
- ncp_add_byte(server, server->name_space[seq->volNumber]);
- ncp_add_byte(server, 0); /* datastream */
- ncp_add_word(server, cpu_to_le16(0x8006));
- ncp_add_dword(server, RIM_ALL);
- ncp_add_word(server, cpu_to_le16(32767)); /* max returned items */
- ncp_add_mem(server, seq, 9);
-#ifdef CONFIG_NCPFS_NFS_NS
- if (server->name_space[seq->volNumber] == NW_NS_NFS) {
- ncp_add_byte(server, 0); /* 0 byte pattern */
- } else
-#endif
- {
- ncp_add_byte(server, 2); /* 2 byte pattern */
- ncp_add_byte(server, 0xff); /* following is a wildcard */
- ncp_add_byte(server, '*');
- }
- result = ncp_request2(server, 87, buffer, bufsize);
- if (result) {
- ncp_unlock_server(server);
- return result;
- }
- if (server->ncp_reply_size < 12) {
- ncp_unlock_server(server);
- return 0xFF;
- }
- *rsize = server->ncp_reply_size - 12;
- ncp_unlock_server(server);
- buffer = buffer + sizeof(struct ncp_reply_header);
- *rbuf = buffer + 12;
- *cnt = WVAL_LH(buffer + 10);
- *more = BVAL(buffer + 9);
- memcpy(seq, buffer, 9);
- return 0;
-}
-
-static int
-ncp_RenameNSEntry(struct ncp_server *server,
- struct inode *old_dir, const char *old_name, __le16 old_type,
- struct inode *new_dir, const char *new_name)
-{
- int result = -EINVAL;
-
- if ((old_dir == NULL) || (old_name == NULL) ||
- (new_dir == NULL) || (new_name == NULL))
- goto out;
-
- ncp_init_request(server);
- ncp_add_byte(server, 4); /* subfunction */
- ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
- ncp_add_byte(server, 1); /* rename flag */
- ncp_add_word(server, old_type); /* search attributes */
-
- /* source Handle Path */
- ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
- ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
- ncp_add_byte(server, 1);
- ncp_add_byte(server, 1); /* 1 source component */
-
- /* dest Handle Path */
- ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
- ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
- ncp_add_byte(server, 1);
- ncp_add_byte(server, 1); /* 1 destination component */
-
- /* source path string */
- ncp_add_pstring(server, old_name);
- /* dest path string */
- ncp_add_pstring(server, new_name);
-
- result = ncp_request(server, 87);
- ncp_unlock_server(server);
-out:
- return result;
-}
-
-int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
- struct inode *old_dir, const char *old_name,
- struct inode *new_dir, const char *new_name)
-{
- int result;
- __le16 old_type = cpu_to_le16(0x06);
-
-/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
- result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
- new_dir, new_name);
- if (result == 0xFF) /* File Not Found, try directory */
- {
- old_type = cpu_to_le16(0x16);
- result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
- new_dir, new_name);
- }
- if (result != 0x92) return result; /* All except NO_FILES_RENAMED */
- result = ncp_del_file_or_subdir(server, new_dir, new_name);
- if (result != 0) return -EACCES;
- result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
- new_dir, new_name);
- return result;
-}
-
-
-/* We have to transfer to/from user space */
-int
-ncp_read_kernel(struct ncp_server *server, const char *file_id,
- __u32 offset, __u16 to_read, char *target, int *bytes_read)
-{
- const char *source;
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 0);
- ncp_add_mem(server, file_id, 6);
- ncp_add_be32(server, offset);
- ncp_add_be16(server, to_read);
-
- if ((result = ncp_request(server, 72)) != 0) {
- goto out;
- }
- *bytes_read = ncp_reply_be16(server, 0);
- source = ncp_reply_data(server, 2 + (offset & 1));
-
- memcpy(target, source, *bytes_read);
-out:
- ncp_unlock_server(server);
- return result;
-}
-
-/* There is a problem... egrep and some other silly tools do:
- x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
- read(<ncpfs fd>, x, 32768);
- Now copying read result by copy_to_user causes pagefault. This pagefault
- could not be handled because of server was locked due to read. So we have
- to use temporary buffer. So ncp_unlock_server must be done before
- copy_to_user (and for write, copy_from_user must be done before
- ncp_init_request... same applies for send raw packet ioctl). Because of
- file is normally read in bigger chunks, caller provides kmalloced
- (vmalloced) chunk of memory with size >= to_read...
- */
-int
-ncp_read_bounce(struct ncp_server *server, const char *file_id,
- __u32 offset, __u16 to_read, struct iov_iter *to,
- int *bytes_read, void *bounce, __u32 bufsize)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 0);
- ncp_add_mem(server, file_id, 6);
- ncp_add_be32(server, offset);
- ncp_add_be16(server, to_read);
- result = ncp_request2(server, 72, bounce, bufsize);
- ncp_unlock_server(server);
- if (!result) {
- int len = get_unaligned_be16((char *)bounce +
- sizeof(struct ncp_reply_header));
- result = -EIO;
- if (len <= to_read) {
- char* source;
-
- source = (char*)bounce +
- sizeof(struct ncp_reply_header) + 2 +
- (offset & 1);
- *bytes_read = len;
- result = 0;
- if (copy_to_iter(source, len, to) != len)
- result = -EFAULT;
- }
- }
- return result;
-}
-
-int
-ncp_write_kernel(struct ncp_server *server, const char *file_id,
- __u32 offset, __u16 to_write,
- const char *source, int *bytes_written)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 0);
- ncp_add_mem(server, file_id, 6);
- ncp_add_be32(server, offset);
- ncp_add_be16(server, to_write);
- ncp_add_mem(server, source, to_write);
-
- if ((result = ncp_request(server, 73)) == 0)
- *bytes_written = to_write;
- ncp_unlock_server(server);
- return result;
-}
-
-#ifdef CONFIG_NCPFS_IOCTL_LOCKING
-int
-ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
- __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, locktype);
- ncp_add_mem(server, file_id, 6);
- ncp_add_be32(server, offset);
- ncp_add_be32(server, length);
- ncp_add_be16(server, timeout);
-
- if ((result = ncp_request(server, 0x1A)) != 0)
- {
- ncp_unlock_server(server);
- return result;
- }
- ncp_unlock_server(server);
- return 0;
-}
-
-int
-ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
- __u32 offset, __u32 length)
-{
- int result;
-
- ncp_init_request(server);
- ncp_add_byte(server, 0); /* who knows... lanalyzer says that */
- ncp_add_mem(server, file_id, 6);
- ncp_add_be32(server, offset);
- ncp_add_be32(server, length);
-
- if ((result = ncp_request(server, 0x1E)) != 0)
- {
- ncp_unlock_server(server);
- return result;
- }
- ncp_unlock_server(server);
- return 0;
-}
-#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
-
-#ifdef CONFIG_NCPFS_NLS
-/* This are the NLS conversion routines with inspirations and code parts
- * from the vfat file system and hints from Petr Vandrovec.
- */
-
-int
-ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
- const unsigned char *iname, unsigned int ilen, int cc)
-{
- struct nls_table *in = server->nls_io;
- struct nls_table *out = server->nls_vol;
- unsigned char *vname_start;
- unsigned char *vname_end;
- const unsigned char *iname_end;
-
- iname_end = iname + ilen;
- vname_start = vname;
- vname_end = vname + *vlen - 1;
-
- while (iname < iname_end) {
- int chl;
- wchar_t ec;
-
- if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
- int k;
- unicode_t u;
-
- k = utf8_to_utf32(iname, iname_end - iname, &u);
- if (k < 0 || u > MAX_WCHAR_T)
- return -EINVAL;
- iname += k;
- ec = u;
- } else {
- if (*iname == NCP_ESC) {
- int k;
-
- if (iname_end - iname < 5)
- goto nospec;
-
- ec = 0;
- for (k = 1; k < 5; k++) {
- unsigned char nc;
-
- nc = iname[k] - '0';
- if (nc >= 10) {
- nc -= 'A' - '0' - 10;
- if ((nc < 10) || (nc > 15)) {
- goto nospec;
- }
- }
- ec = (ec << 4) | nc;
- }
- iname += 5;
- } else {
-nospec:;
- if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0)
- return chl;
- iname += chl;
- }
- }
-
- /* unitoupper should be here! */
-
- chl = out->uni2char(ec, vname, vname_end - vname);
- if (chl < 0)
- return chl;
-
- /* this is wrong... */
- if (cc) {
- int chi;
-
- for (chi = 0; chi < chl; chi++){
- vname[chi] = ncp_toupper(out, vname[chi]);
- }
- }
- vname += chl;
- }
-
- *vname = 0;
- *vlen = vname - vname_start;
- return 0;
-}
-
-int
-ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
- const unsigned char *vname, unsigned int vlen, int cc)
-{
- struct nls_table *in = server->nls_vol;
- struct nls_table *out = server->nls_io;
- const unsigned char *vname_end;
- unsigned char *iname_start;
- unsigned char *iname_end;
- unsigned char *vname_cc;
- int err;
-
- vname_cc = NULL;
-
- if (cc) {
- int i;
-
- /* this is wrong! */
- vname_cc = kmalloc(vlen, GFP_KERNEL);
- if (!vname_cc)
- return -ENOMEM;
- for (i = 0; i < vlen; i++)
- vname_cc[i] = ncp_tolower(in, vname[i]);
- vname = vname_cc;
- }
-
- iname_start = iname;
- iname_end = iname + *ilen - 1;
- vname_end = vname + vlen;
-
- while (vname < vname_end) {
- wchar_t ec;
- int chl;
-
- if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) {
- err = chl;
- goto quit;
- }
- vname += chl;
-
- /* unitolower should be here! */
-
- if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
- int k;
-
- k = utf32_to_utf8(ec, iname, iname_end - iname);
- if (k < 0) {
- err = -ENAMETOOLONG;
- goto quit;
- }
- iname += k;
- } else {
- if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) {
- iname += chl;
- } else {
- int k;
-
- if (iname_end - iname < 5) {
- err = -ENAMETOOLONG;
- goto quit;
- }
- *iname = NCP_ESC;
- for (k = 4; k > 0; k--) {
- unsigned char v;
-
- v = (ec & 0xF) + '0';
- if (v > '9') {
- v += 'A' - '9' - 1;
- }
- iname[k] = v;
- ec >>= 4;
- }
- iname += 5;
- }
- }
- }
-
- *iname = 0;
- *ilen = iname - iname_start;
- err = 0;
-quit:;
- if (cc)
- kfree(vname_cc);
- return err;
-}
-
-#else
-
-int
-ncp__io2vol(unsigned char *vname, unsigned int *vlen,
- const unsigned char *iname, unsigned int ilen, int cc)
-{
- int i;
-
- if (*vlen <= ilen)
- return -ENAMETOOLONG;
-
- if (cc)
- for (i = 0; i < ilen; i++) {
- *vname = toupper(*iname);
- vname++;
- iname++;
- }
- else {
- memmove(vname, iname, ilen);
- vname += ilen;
- }
-
- *vlen = ilen;
- *vname = 0;
- return 0;
-}
-
-int
-ncp__vol2io(unsigned char *iname, unsigned int *ilen,
- const unsigned char *vname, unsigned int vlen, int cc)
-{
- int i;
-
- if (*ilen <= vlen)
- return -ENAMETOOLONG;
-
- if (cc)
- for (i = 0; i < vlen; i++) {
- *iname = tolower(*vname);
- iname++;
- vname++;
- }
- else {
- memmove(iname, vname, vlen);
- iname += vlen;
- }
-
- *ilen = vlen;
- *iname = 0;
- return 0;
-}
-
-#endif
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
deleted file mode 100644
index aaae8aa9bf7d..000000000000
--- a/fs/ncpfs/ncplib_kernel.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ncplib_kernel.h
- *
- * Copyright (C) 1995, 1996 by Volker Lendecke
- * Modified for big endian by J.F. Chadima and David S. Miller
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- * Modified 1998, 1999 Wolfram Pienkoss for NLS
- * Modified 1999 Wolfram Pienkoss for directory caching
- *
- */
-
-#ifndef _NCPLIB_H
-#define _NCPLIB_H
-
-
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
-#include <linux/pagemap.h>
-
-#include <linux/uaccess.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-#include <asm/string.h>
-
-#ifdef CONFIG_NCPFS_NLS
-#include <linux/nls.h>
-#else
-#include <linux/ctype.h>
-#endif /* CONFIG_NCPFS_NLS */
-
-#define NCP_MIN_SYMLINK_SIZE 8
-#define NCP_MAX_SYMLINK_SIZE 512
-
-#define NCP_BLOCK_SHIFT 9
-#define NCP_BLOCK_SIZE (1 << (NCP_BLOCK_SHIFT))
-
-int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
-int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
- int options, int *ret_size, int *ret_options);
-
-int ncp_get_volume_info_with_number(struct ncp_server* server, int n,
- struct ncp_volume_info *target);
-
-int ncp_get_directory_info(struct ncp_server* server, __u8 dirhandle,
- struct ncp_volume_info* target);
-
-int ncp_close_file(struct ncp_server *, const char *);
-static inline int ncp_read_bounce_size(__u32 size) {
- return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8;
-};
-int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16,
- struct iov_iter *, int *, void *bounce, __u32 bouncelen);
-int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16,
- char *, int *);
-int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
- const char *, int *);
-
-static inline void ncp_inode_close(struct inode *inode) {
- atomic_dec(&NCP_FINFO(inode)->opened);
-}
-
-void ncp_extract_file_info(const void* src, struct nw_info_struct* target);
-int ncp_obtain_info(struct ncp_server *server, struct inode *, const char *,
- struct nw_info_struct *target);
-int ncp_obtain_nfs_info(struct ncp_server *server, struct nw_info_struct *target);
-int ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns);
-int ncp_get_volume_root(struct ncp_server *server, const char *volname,
- __u32 *volume, __le32 *dirent, __le32 *dosdirent);
-int ncp_lookup_volume(struct ncp_server *, const char *, struct nw_info_struct *);
-int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
- __le32, const struct nw_modify_dos_info *info);
-int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *,
- const char* path, __le32, const struct nw_modify_dos_info *info);
-int ncp_modify_nfs_info(struct ncp_server *, __u8 volnum, __le32 dirent,
- __u32 mode, __u32 rdev);
-
-int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
-int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, const char *);
-int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, const char *,
- int, __le32, __le16, struct ncp_entry_info *);
-
-int ncp_initialize_search(struct ncp_server *, struct inode *,
- struct nw_search_sequence *target);
-int ncp_search_for_fileset(struct ncp_server *server,
- struct nw_search_sequence *seq,
- int* more, int* cnt,
- char* buffer, size_t bufsize,
- char** rbuf, size_t* rsize);
-
-int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
- struct inode *, const char *, struct inode *, const char *);
-
-
-int
-ncp_LogPhysicalRecord(struct ncp_server *server,
- const char *file_id, __u8 locktype,
- __u32 offset, __u32 length, __u16 timeout);
-
-#ifdef CONFIG_NCPFS_IOCTL_LOCKING
-int
-ncp_ClearPhysicalRecord(struct ncp_server *server,
- const char *file_id,
- __u32 offset, __u32 length);
-#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
-
-int
-ncp_mount_subdir(struct ncp_server *, __u8, __u8, __le32,
- __u32* volume, __le32* dirent, __le32* dosdirent);
-int ncp_dirhandle_alloc(struct ncp_server *, __u8 vol, __le32 dirent, __u8 *dirhandle);
-int ncp_dirhandle_free(struct ncp_server *, __u8 dirhandle);
-
-int ncp_create_new(struct inode *dir, struct dentry *dentry,
- umode_t mode, dev_t rdev, __le32 attributes);
-
-static inline int ncp_is_nfs_extras(struct ncp_server* server, unsigned int volnum) {
-#ifdef CONFIG_NCPFS_NFS_NS
- return (server->m.flags & NCP_MOUNT_NFS_EXTRAS) &&
- (server->name_space[volnum] == NW_NS_NFS);
-#else
- return 0;
-#endif
-}
-
-#ifdef CONFIG_NCPFS_NLS
-
-int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *,
- const unsigned char *, unsigned int, int);
-int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *,
- const unsigned char *, unsigned int, int);
-
-#define NCP_ESC ':'
-#define NCP_IO_TABLE(sb) (NCP_SBP(sb)->nls_io)
-#define ncp_tolower(t, c) nls_tolower(t, c)
-#define ncp_toupper(t, c) nls_toupper(t, c)
-#define ncp_strnicmp(t, s1, s2, len) \
- nls_strnicmp(t, s1, s2, len)
-#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(S,m,i,n,k,U)
-#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(S,m,i,n,k,U)
-
-#else
-
-int ncp__io2vol(unsigned char *, unsigned int *,
- const unsigned char *, unsigned int, int);
-int ncp__vol2io(unsigned char *, unsigned int *,
- const unsigned char *, unsigned int, int);
-
-#define NCP_IO_TABLE(sb) NULL
-#define ncp_tolower(t, c) tolower(c)
-#define ncp_toupper(t, c) toupper(c)
-#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(m,i,n,k,U)
-#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(m,i,n,k,U)
-
-
-static inline int ncp_strnicmp(const struct nls_table *t,
- const unsigned char *s1, const unsigned char *s2, int len)
-{
- while (len--) {
- if (tolower(*s1++) != tolower(*s2++))
- return 1;
- }
-
- return 0;
-}
-
-#endif /* CONFIG_NCPFS_NLS */
-
-#define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time)
-#define NCP_MAX_AGE(server) atomic_read(&(server)->dentry_ttl)
-#define NCP_TEST_AGE(server,dentry) (NCP_GET_AGE(dentry) < NCP_MAX_AGE(server))
-
-static inline void
-ncp_age_dentry(struct ncp_server* server, struct dentry* dentry)
-{
- dentry->d_time = jiffies - NCP_MAX_AGE(server);
-}
-
-static inline void
-ncp_new_dentry(struct dentry* dentry)
-{
- dentry->d_time = jiffies;
-}
-
-struct ncp_cache_head {
- time_t mtime;
- unsigned long time; /* cache age */
- unsigned long end; /* last valid fpos in cache */
- int eof;
-};
-
-#define NCP_DIRCACHE_SIZE ((int)(PAGE_SIZE/sizeof(struct dentry *)))
-union ncp_dir_cache {
- struct ncp_cache_head head;
- struct dentry *dentry[NCP_DIRCACHE_SIZE];
-};
-
-#define NCP_FIRSTCACHE_SIZE ((int)((NCP_DIRCACHE_SIZE * \
- sizeof(struct dentry *) - sizeof(struct ncp_cache_head)) / \
- sizeof(struct dentry *)))
-
-#define NCP_DIRCACHE_START (NCP_DIRCACHE_SIZE - NCP_FIRSTCACHE_SIZE)
-
-struct ncp_cache_control {
- struct ncp_cache_head head;
- struct page *page;
- union ncp_dir_cache *cache;
- unsigned long fpos, ofs;
- int filled, valid, idx;
-};
-
-#endif /* _NCPLIB_H */
diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c
deleted file mode 100644
index 8085b1a3ba47..000000000000
--- a/fs/ncpfs/ncpsign_kernel.c
+++ /dev/null
@@ -1,128 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ncpsign_kernel.c
- *
- * Arne de Bruijn (arne@knoware.nl), 1997
- *
- */
-
-
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
-
-#include <linux/string.h>
-#include <linux/ncp.h>
-#include <linux/bitops.h>
-#include "ncp_fs.h"
-#include "ncpsign_kernel.h"
-
-/* i386: 32-bit, little endian, handles mis-alignment */
-#ifdef __i386__
-#define GET_LE32(p) (*(const int *)(p))
-#define PUT_LE32(p,v) { *(int *)(p)=v; }
-#else
-/* from include/ncplib.h */
-#define BVAL(buf,pos) (((const __u8 *)(buf))[pos])
-#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
-#define BSET(buf,pos,val) (((__u8 *)(buf))[pos] = (val))
-
-static inline __u16
-WVAL_LH(const __u8 * buf, int pos)
-{
- return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
-}
-static inline __u32
-DVAL_LH(const __u8 * buf, int pos)
-{
- return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
-}
-static inline void
-WSET_LH(__u8 * buf, int pos, __u16 val)
-{
- BSET(buf, pos, val & 0xff);
- BSET(buf, pos + 1, val >> 8);
-}
-static inline void
-DSET_LH(__u8 * buf, int pos, __u32 val)
-{
- WSET_LH(buf, pos, val & 0xffff);
- WSET_LH(buf, pos + 2, val >> 16);
-}
-
-#define GET_LE32(p) DVAL_LH(p,0)
-#define PUT_LE32(p,v) DSET_LH(p,0,v)
-#endif
-
-static void nwsign(char *r_data1, char *r_data2, char *outdata) {
- int i;
- unsigned int w0,w1,w2,w3;
- static int rbit[4]={0, 2, 1, 3};
-#ifdef __i386__
- unsigned int *data2=(unsigned int *)r_data2;
-#else
- unsigned int data2[16];
- for (i=0;i<16;i++)
- data2[i]=GET_LE32(r_data2+(i<<2));
-#endif
- w0=GET_LE32(r_data1);
- w1=GET_LE32(r_data1+4);
- w2=GET_LE32(r_data1+8);
- w3=GET_LE32(r_data1+12);
- for (i=0;i<16;i+=4) {
- w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3);
- w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7);
- w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11);
- w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19);
- }
- for (i=0;i<4;i++) {
- w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3);
- w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5);
- w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9);
- w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13);
- }
- for (i=0;i<4;i++) {
- w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3);
- w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9);
- w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11);
- w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15);
- }
- PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff);
- PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff);
- PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff);
- PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff);
-}
-
-/* Make a signature for the current packet and add it at the end of the */
-/* packet. */
-void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) {
- unsigned char data[64];
-
- memcpy(data, server->sign_root, 8);
- *(__u32*)(data + 8) = totalsize;
- if (size < 52) {
- memcpy(data + 12, packet, size);
- memset(data + 12 + size, 0, 52 - size);
- } else {
- memcpy(data + 12, packet, 52);
- }
- nwsign(server->sign_last, data, server->sign_last);
- memcpy(sign_buff, server->sign_last, 8);
-}
-
-int sign_verify_reply(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, const void *sign_buff) {
- unsigned char data[64];
- unsigned char hash[16];
-
- memcpy(data, server->sign_root, 8);
- *(__u32*)(data + 8) = totalsize;
- if (size < 52) {
- memcpy(data + 12, packet, size);
- memset(data + 12 + size, 0, 52 - size);
- } else {
- memcpy(data + 12, packet, 52);
- }
- nwsign(server->sign_last, data, hash);
- return memcmp(sign_buff, hash, 8);
-}
-
-#endif /* CONFIG_NCPFS_PACKET_SIGNING */
-
diff --git a/fs/ncpfs/ncpsign_kernel.h b/fs/ncpfs/ncpsign_kernel.h
deleted file mode 100644
index 57ff0a0650b8..000000000000
--- a/fs/ncpfs/ncpsign_kernel.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ncpsign_kernel.h
- *
- * Arne de Bruijn (arne@knoware.nl), 1997
- *
- */
-
-#ifndef _NCPSIGN_KERNEL_H
-#define _NCPSIGN_KERNEL_H
-
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
-void __sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff);
-int sign_verify_reply(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, const void *sign_buff);
-#endif
-
-static inline size_t sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff) {
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- if (server->sign_active) {
- __sign_packet(server, data, size, totalsize, sign_buff);
- return 8;
- }
-#endif
- return 0;
-}
-
-#endif
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
deleted file mode 100644
index 4c13174d85b7..000000000000
--- a/fs/ncpfs/sock.c
+++ /dev/null
@@ -1,855 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/fs/ncpfs/sock.c
- *
- * Copyright (C) 1992, 1993 Rick Sladkey
- *
- * Modified 1995, 1996 by Volker Lendecke to be usable for ncp
- * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/time.h>
-#include <linux/errno.h>
-#include <linux/socket.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/sched/signal.h>
-#include <linux/uaccess.h>
-#include <linux/in.h>
-#include <linux/net.h>
-#include <linux/mm.h>
-#include <linux/netdevice.h>
-#include <linux/signal.h>
-#include <linux/slab.h>
-#include <net/scm.h>
-#include <net/sock.h>
-#include <linux/ipx.h>
-#include <linux/poll.h>
-#include <linux/file.h>
-
-#include "ncp_fs.h"
-
-#include "ncpsign_kernel.h"
-
-static int _recv(struct socket *sock, void *buf, int size, unsigned flags)
-{
- struct msghdr msg = {NULL, };
- struct kvec iov = {buf, size};
- iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC, &iov, 1, size);
- return sock_recvmsg(sock, &msg, flags);
-}
-
-static int _send(struct socket *sock, const void *buff, int len)
-{
- struct msghdr msg = { .msg_flags = 0 };
- struct kvec vec = {.iov_base = (void *)buff, .iov_len = len};
- iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &vec, 1, len);
- return sock_sendmsg(sock, &msg);
-}
-
-struct ncp_request_reply {
- struct list_head req;
- wait_queue_head_t wq;
- atomic_t refs;
- unsigned char* reply_buf;
- size_t datalen;
- int result;
- enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE, RQ_ABANDONED } status;
- struct iov_iter from;
- struct kvec tx_iov[3];
- u_int16_t tx_type;
- u_int32_t sign[6];
-};
-
-static inline struct ncp_request_reply* ncp_alloc_req(void)
-{
- struct ncp_request_reply *req;
-
- req = kmalloc(sizeof(struct ncp_request_reply), GFP_KERNEL);
- if (!req)
- return NULL;
-
- init_waitqueue_head(&req->wq);
- atomic_set(&req->refs, (1));
- req->status = RQ_IDLE;
-
- return req;
-}
-
-static void ncp_req_get(struct ncp_request_reply *req)
-{
- atomic_inc(&req->refs);
-}
-
-static void ncp_req_put(struct ncp_request_reply *req)
-{
- if (atomic_dec_and_test(&req->refs))
- kfree(req);
-}
-
-void ncp_tcp_data_ready(struct sock *sk)
-{
- struct ncp_server *server = sk->sk_user_data;
-
- server->data_ready(sk);
- schedule_work(&server->rcv.tq);
-}
-
-void ncp_tcp_error_report(struct sock *sk)
-{
- struct ncp_server *server = sk->sk_user_data;
-
- server->error_report(sk);
- schedule_work(&server->rcv.tq);
-}
-
-void ncp_tcp_write_space(struct sock *sk)
-{
- struct ncp_server *server = sk->sk_user_data;
-
- /* We do not need any locking: we first set tx.creq, and then we do sendmsg,
- not vice versa... */
- server->write_space(sk);
- if (server->tx.creq)
- schedule_work(&server->tx.tq);
-}
-
-void ncpdgram_timeout_call(struct timer_list *t)
-{
- struct ncp_server *server = from_timer(server, t, timeout_tm);
-
- schedule_work(&server->timeout_tq);
-}
-
-static inline void ncp_finish_request(struct ncp_server *server, struct ncp_request_reply *req, int result)
-{
- req->result = result;
- if (req->status != RQ_ABANDONED)
- memcpy(req->reply_buf, server->rxbuf, req->datalen);
- req->status = RQ_DONE;
- wake_up_all(&req->wq);
- ncp_req_put(req);
-}
-
-static void __abort_ncp_connection(struct ncp_server *server)
-{
- struct ncp_request_reply *req;
-
- ncp_invalidate_conn(server);
- del_timer(&server->timeout_tm);
- while (!list_empty(&server->tx.requests)) {
- req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
-
- list_del_init(&req->req);
- ncp_finish_request(server, req, -EIO);
- }
- req = server->rcv.creq;
- if (req) {
- server->rcv.creq = NULL;
- ncp_finish_request(server, req, -EIO);
- server->rcv.ptr = NULL;
- server->rcv.state = 0;
- }
- req = server->tx.creq;
- if (req) {
- server->tx.creq = NULL;
- ncp_finish_request(server, req, -EIO);
- }
-}
-
-static inline int get_conn_number(struct ncp_reply_header *rp)
-{
- return rp->conn_low | (rp->conn_high << 8);
-}
-
-static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
-{
- /* If req is done, we got signal, but we also received answer... */
- switch (req->status) {
- case RQ_IDLE:
- case RQ_DONE:
- break;
- case RQ_QUEUED:
- list_del_init(&req->req);
- ncp_finish_request(server, req, err);
- break;
- case RQ_INPROGRESS:
- req->status = RQ_ABANDONED;
- break;
- case RQ_ABANDONED:
- break;
- }
-}
-
-static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
-{
- mutex_lock(&server->rcv.creq_mutex);
- __ncp_abort_request(server, req, err);
- mutex_unlock(&server->rcv.creq_mutex);
-}
-
-static inline void __ncptcp_abort(struct ncp_server *server)
-{
- __abort_ncp_connection(server);
-}
-
-static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req)
-{
- struct msghdr msg = { .msg_iter = req->from, .msg_flags = MSG_DONTWAIT };
- return sock_sendmsg(sock, &msg);
-}
-
-static void __ncptcp_try_send(struct ncp_server *server)
-{
- struct ncp_request_reply *rq;
- struct msghdr msg = { .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT };
- int result;
-
- rq = server->tx.creq;
- if (!rq)
- return;
-
- msg.msg_iter = rq->from;
- result = sock_sendmsg(server->ncp_sock, &msg);
-
- if (result == -EAGAIN)
- return;
-
- if (result < 0) {
- pr_err("tcp: Send failed: %d\n", result);
- __ncp_abort_request(server, rq, result);
- return;
- }
- if (!msg_data_left(&msg)) {
- server->rcv.creq = rq;
- server->tx.creq = NULL;
- return;
- }
- rq->from = msg.msg_iter;
-}
-
-static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h)
-{
- req->status = RQ_INPROGRESS;
- h->conn_low = server->connection;
- h->conn_high = server->connection >> 8;
- h->sequence = ++server->sequence;
-}
-
-static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req)
-{
- size_t signlen, len = req->tx_iov[1].iov_len;
- struct ncp_request_header *h = req->tx_iov[1].iov_base;
-
- ncp_init_header(server, req, h);
- signlen = sign_packet(server,
- req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
- len - sizeof(struct ncp_request_header) + 1,
- cpu_to_le32(len), req->sign);
- if (signlen) {
- /* NCP over UDP appends signature */
- req->tx_iov[2].iov_base = req->sign;
- req->tx_iov[2].iov_len = signlen;
- }
- iov_iter_kvec(&req->from, WRITE | ITER_KVEC,
- req->tx_iov + 1, signlen ? 2 : 1, len + signlen);
- server->rcv.creq = req;
- server->timeout_last = server->m.time_out;
- server->timeout_retries = server->m.retry_count;
- ncpdgram_send(server->ncp_sock, req);
- mod_timer(&server->timeout_tm, jiffies + server->m.time_out);
-}
-
-#define NCP_TCP_XMIT_MAGIC (0x446D6454)
-#define NCP_TCP_XMIT_VERSION (1)
-#define NCP_TCP_RCVD_MAGIC (0x744E6350)
-
-static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
-{
- size_t signlen, len = req->tx_iov[1].iov_len;
- struct ncp_request_header *h = req->tx_iov[1].iov_base;
-
- ncp_init_header(server, req, h);
- signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
- len - sizeof(struct ncp_request_header) + 1,
- cpu_to_be32(len + 24), req->sign + 4) + 16;
-
- req->sign[0] = htonl(NCP_TCP_XMIT_MAGIC);
- req->sign[1] = htonl(len + signlen);
- req->sign[2] = htonl(NCP_TCP_XMIT_VERSION);
- req->sign[3] = htonl(req->datalen + 8);
- /* NCP over TCP prepends signature */
- req->tx_iov[0].iov_base = req->sign;
- req->tx_iov[0].iov_len = signlen;
- iov_iter_kvec(&req->from, WRITE | ITER_KVEC,
- req->tx_iov, 2, len + signlen);
-
- server->tx.creq = req;
- __ncptcp_try_send(server);
-}
-
-static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
-{
- /* we copy the data so that we do not depend on the caller
- staying alive */
- memcpy(server->txbuf, req->tx_iov[1].iov_base, req->tx_iov[1].iov_len);
- req->tx_iov[1].iov_base = server->txbuf;
-
- if (server->ncp_sock->type == SOCK_STREAM)
- ncptcp_start_request(server, req);
- else
- ncpdgram_start_request(server, req);
-}
-
-static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req)
-{
- mutex_lock(&server->rcv.creq_mutex);
- if (!ncp_conn_valid(server)) {
- mutex_unlock(&server->rcv.creq_mutex);
- pr_err("tcp: Server died\n");
- return -EIO;
- }
- ncp_req_get(req);
- if (server->tx.creq || server->rcv.creq) {
- req->status = RQ_QUEUED;
- list_add_tail(&req->req, &server->tx.requests);
- mutex_unlock(&server->rcv.creq_mutex);
- return 0;
- }
- __ncp_start_request(server, req);
- mutex_unlock(&server->rcv.creq_mutex);
- return 0;
-}
-
-static void __ncp_next_request(struct ncp_server *server)
-{
- struct ncp_request_reply *req;
-
- server->rcv.creq = NULL;
- if (list_empty(&server->tx.requests)) {
- return;
- }
- req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
- list_del_init(&req->req);
- __ncp_start_request(server, req);
-}
-
-static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len)
-{
- if (server->info_sock) {
- struct msghdr msg = { .msg_flags = MSG_NOSIGNAL };
- __be32 hdr[2] = {cpu_to_be32(len + 8), cpu_to_be32(id)};
- struct kvec iov[2] = {
- {.iov_base = hdr, .iov_len = 8},
- {.iov_base = (void *)data, .iov_len = len},
- };
-
- iov_iter_kvec(&msg.msg_iter, ITER_KVEC | WRITE,
- iov, 2, len + 8);
-
- sock_sendmsg(server->info_sock, &msg);
- }
-}
-
-void ncpdgram_rcv_proc(struct work_struct *work)
-{
- struct ncp_server *server =
- container_of(work, struct ncp_server, rcv.tq);
- struct socket* sock;
-
- sock = server->ncp_sock;
-
- while (1) {
- struct ncp_reply_header reply;
- int result;
-
- result = _recv(sock, &reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
- if (result < 0) {
- break;
- }
- if (result >= sizeof(reply)) {
- struct ncp_request_reply *req;
-
- if (reply.type == NCP_WATCHDOG) {
- unsigned char buf[10];
-
- if (server->connection != get_conn_number(&reply)) {
- goto drop;
- }
- result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
- if (result < 0) {
- ncp_dbg(1, "recv failed with %d\n", result);
- continue;
- }
- if (result < 10) {
- ncp_dbg(1, "too short (%u) watchdog packet\n", result);
- continue;
- }
- if (buf[9] != '?') {
- ncp_dbg(1, "bad signature (%02X) in watchdog packet\n", buf[9]);
- continue;
- }
- buf[9] = 'Y';
- _send(sock, buf, sizeof(buf));
- continue;
- }
- if (reply.type != NCP_POSITIVE_ACK && reply.type != NCP_REPLY) {
- result = _recv(sock, server->unexpected_packet.data, sizeof(server->unexpected_packet.data), MSG_DONTWAIT);
- if (result < 0) {
- continue;
- }
- info_server(server, 0, server->unexpected_packet.data, result);
- continue;
- }
- mutex_lock(&server->rcv.creq_mutex);
- req = server->rcv.creq;
- if (req && (req->tx_type == NCP_ALLOC_SLOT_REQUEST || (server->sequence == reply.sequence &&
- server->connection == get_conn_number(&reply)))) {
- if (reply.type == NCP_POSITIVE_ACK) {
- server->timeout_retries = server->m.retry_count;
- server->timeout_last = NCP_MAX_RPC_TIMEOUT;
- mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT);
- } else if (reply.type == NCP_REPLY) {
- result = _recv(sock, server->rxbuf, req->datalen, MSG_DONTWAIT);
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) {
- if (result < 8 + 8) {
- result = -EIO;
- } else {
- unsigned int hdrl;
-
- result -= 8;
- hdrl = sock->sk->sk_family == AF_INET ? 8 : 6;
- if (sign_verify_reply(server, server->rxbuf + hdrl, result - hdrl, cpu_to_le32(result), server->rxbuf + result)) {
- pr_info("Signature violation\n");
- result = -EIO;
- }
- }
- }
-#endif
- del_timer(&server->timeout_tm);
- server->rcv.creq = NULL;
- ncp_finish_request(server, req, result);
- __ncp_next_request(server);
- mutex_unlock(&server->rcv.creq_mutex);
- continue;
- }
- }
- mutex_unlock(&server->rcv.creq_mutex);
- }
-drop:;
- _recv(sock, &reply, sizeof(reply), MSG_DONTWAIT);
- }
-}
-
-static void __ncpdgram_timeout_proc(struct ncp_server *server)
-{
- /* If timer is pending, we are processing another request... */
- if (!timer_pending(&server->timeout_tm)) {
- struct ncp_request_reply* req;
-
- req = server->rcv.creq;
- if (req) {
- int timeout;
-
- if (server->m.flags & NCP_MOUNT_SOFT) {
- if (server->timeout_retries-- == 0) {
- __ncp_abort_request(server, req, -ETIMEDOUT);
- return;
- }
- }
- /* Ignore errors */
- ncpdgram_send(server->ncp_sock, req);
- timeout = server->timeout_last << 1;
- if (timeout > NCP_MAX_RPC_TIMEOUT) {
- timeout = NCP_MAX_RPC_TIMEOUT;
- }
- server->timeout_last = timeout;
- mod_timer(&server->timeout_tm, jiffies + timeout);
- }
- }
-}
-
-void ncpdgram_timeout_proc(struct work_struct *work)
-{
- struct ncp_server *server =
- container_of(work, struct ncp_server, timeout_tq);
- mutex_lock(&server->rcv.creq_mutex);
- __ncpdgram_timeout_proc(server);
- mutex_unlock(&server->rcv.creq_mutex);
-}
-
-static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len)
-{
- int result;
-
- if (buffer) {
- result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
- } else {
- static unsigned char dummy[1024];
-
- if (len > sizeof(dummy)) {
- len = sizeof(dummy);
- }
- result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT);
- }
- if (result < 0) {
- return result;
- }
- if (result > len) {
- pr_err("tcp: bug in recvmsg (%u > %zu)\n", result, len);
- return -EIO;
- }
- return result;
-}
-
-static int __ncptcp_rcv_proc(struct ncp_server *server)
-{
- /* We have to check the result, so store the complete header */
- while (1) {
- int result;
- struct ncp_request_reply *req;
- int datalen;
- int type;
-
- while (server->rcv.len) {
- result = do_tcp_rcv(server, server->rcv.ptr, server->rcv.len);
- if (result == -EAGAIN) {
- return 0;
- }
- if (result <= 0) {
- req = server->rcv.creq;
- if (req) {
- __ncp_abort_request(server, req, -EIO);
- } else {
- __ncptcp_abort(server);
- }
- if (result < 0) {
- pr_err("tcp: error in recvmsg: %d\n", result);
- } else {
- ncp_dbg(1, "tcp: EOF\n");
- }
- return -EIO;
- }
- if (server->rcv.ptr) {
- server->rcv.ptr += result;
- }
- server->rcv.len -= result;
- }
- switch (server->rcv.state) {
- case 0:
- if (server->rcv.buf.magic != htonl(NCP_TCP_RCVD_MAGIC)) {
- pr_err("tcp: Unexpected reply type %08X\n", ntohl(server->rcv.buf.magic));
- __ncptcp_abort(server);
- return -EIO;
- }
- datalen = ntohl(server->rcv.buf.len) & 0x0FFFFFFF;
- if (datalen < 10) {
- pr_err("tcp: Unexpected reply len %d\n", datalen);
- __ncptcp_abort(server);
- return -EIO;
- }
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- if (server->sign_active) {
- if (datalen < 18) {
- pr_err("tcp: Unexpected reply len %d\n", datalen);
- __ncptcp_abort(server);
- return -EIO;
- }
- server->rcv.buf.len = datalen - 8;
- server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1;
- server->rcv.len = 8;
- server->rcv.state = 4;
- break;
- }
-#endif
- type = ntohs(server->rcv.buf.type);
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
-cont:;
-#endif
- if (type != NCP_REPLY) {
- if (datalen - 8 <= sizeof(server->unexpected_packet.data)) {
- *(__u16*)(server->unexpected_packet.data) = htons(type);
- server->unexpected_packet.len = datalen - 8;
-
- server->rcv.state = 5;
- server->rcv.ptr = server->unexpected_packet.data + 2;
- server->rcv.len = datalen - 10;
- break;
- }
- ncp_dbg(1, "tcp: Unexpected NCP type %02X\n", type);
-skipdata2:;
- server->rcv.state = 2;
-skipdata:;
- server->rcv.ptr = NULL;
- server->rcv.len = datalen - 10;
- break;
- }
- req = server->rcv.creq;
- if (!req) {
- ncp_dbg(1, "Reply without appropriate request\n");
- goto skipdata2;
- }
- if (datalen > req->datalen + 8) {
- pr_err("tcp: Unexpected reply len %d (expected at most %zd)\n", datalen, req->datalen + 8);
- server->rcv.state = 3;
- goto skipdata;
- }
- req->datalen = datalen - 8;
- ((struct ncp_reply_header*)server->rxbuf)->type = NCP_REPLY;
- server->rcv.ptr = server->rxbuf + 2;
- server->rcv.len = datalen - 10;
- server->rcv.state = 1;
- break;
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- case 4:
- datalen = server->rcv.buf.len;
- type = ntohs(server->rcv.buf.type2);
- goto cont;
-#endif
- case 1:
- req = server->rcv.creq;
- if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) {
- if (((struct ncp_reply_header*)server->rxbuf)->sequence != server->sequence) {
- pr_err("tcp: Bad sequence number\n");
- __ncp_abort_request(server, req, -EIO);
- return -EIO;
- }
- if ((((struct ncp_reply_header*)server->rxbuf)->conn_low | (((struct ncp_reply_header*)server->rxbuf)->conn_high << 8)) != server->connection) {
- pr_err("tcp: Connection number mismatch\n");
- __ncp_abort_request(server, req, -EIO);
- return -EIO;
- }
- }
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
- if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) {
- if (sign_verify_reply(server, server->rxbuf + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) {
- pr_err("tcp: Signature violation\n");
- __ncp_abort_request(server, req, -EIO);
- return -EIO;
- }
- }
-#endif
- ncp_finish_request(server, req, req->datalen);
- nextreq:;
- __ncp_next_request(server);
- case 2:
- next:;
- server->rcv.ptr = (unsigned char*)&server->rcv.buf;
- server->rcv.len = 10;
- server->rcv.state = 0;
- break;
- case 3:
- ncp_finish_request(server, server->rcv.creq, -EIO);
- goto nextreq;
- case 5:
- info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len);
- goto next;
- }
- }
-}
-
-void ncp_tcp_rcv_proc(struct work_struct *work)
-{
- struct ncp_server *server =
- container_of(work, struct ncp_server, rcv.tq);
-
- mutex_lock(&server->rcv.creq_mutex);
- __ncptcp_rcv_proc(server);
- mutex_unlock(&server->rcv.creq_mutex);
-}
-
-void ncp_tcp_tx_proc(struct work_struct *work)
-{
- struct ncp_server *server =
- container_of(work, struct ncp_server, tx.tq);
-
- mutex_lock(&server->rcv.creq_mutex);
- __ncptcp_try_send(server);
- mutex_unlock(&server->rcv.creq_mutex);
-}
-
-static int do_ncp_rpc_call(struct ncp_server *server, int size,
- unsigned char* reply_buf, int max_reply_size)
-{
- int result;
- struct ncp_request_reply *req;
-
- req = ncp_alloc_req();
- if (!req)
- return -ENOMEM;
-
- req->reply_buf = reply_buf;
- req->datalen = max_reply_size;
- req->tx_iov[1].iov_base = server->packet;
- req->tx_iov[1].iov_len = size;
- req->tx_type = *(u_int16_t*)server->packet;
-
- result = ncp_add_request(server, req);
- if (result < 0)
- goto out;
-
- if (wait_event_interruptible(req->wq, req->status == RQ_DONE)) {
- ncp_abort_request(server, req, -EINTR);
- result = -EINTR;
- goto out;
- }
-
- result = req->result;
-
-out:
- ncp_req_put(req);
-
- return result;
-}
-
-/*
- * We need the server to be locked here, so check!
- */
-
-static int ncp_do_request(struct ncp_server *server, int size,
- void* reply, int max_reply_size)
-{
- int result;
-
- if (server->lock == 0) {
- pr_err("Server not locked!\n");
- return -EIO;
- }
- if (!ncp_conn_valid(server)) {
- return -EIO;
- }
- {
- sigset_t old_set;
- unsigned long mask, flags;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- old_set = current->blocked;
- if (current->flags & PF_EXITING)
- mask = 0;
- else
- mask = sigmask(SIGKILL);
- if (server->m.flags & NCP_MOUNT_INTR) {
- /* FIXME: This doesn't seem right at all. So, like,
- we can't handle SIGINT and get whatever to stop?
- What if we've blocked it ourselves? What about
- alarms? Why, in fact, are we mucking with the
- sigmask at all? -- r~ */
- if (current->sighand->action[SIGINT - 1].sa.sa_handler == SIG_DFL)
- mask |= sigmask(SIGINT);
- if (current->sighand->action[SIGQUIT - 1].sa.sa_handler == SIG_DFL)
- mask |= sigmask(SIGQUIT);
- }
- siginitsetinv(&current->blocked, mask);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
- result = do_ncp_rpc_call(server, size, reply, max_reply_size);
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = old_set;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
- }
-
- ncp_dbg(2, "do_ncp_rpc_call returned %d\n", result);
-
- return result;
-}
-
-/* ncp_do_request assures that at least a complete reply header is
- * received. It assumes that server->current_size contains the ncp
- * request size
- */
-int ncp_request2(struct ncp_server *server, int function,
- void* rpl, int size)
-{
- struct ncp_request_header *h;
- struct ncp_reply_header* reply = rpl;
- int result;
-
- h = (struct ncp_request_header *) (server->packet);
- if (server->has_subfunction != 0) {
- *(__u16 *) & (h->data[0]) = htons(server->current_size - sizeof(*h) - 2);
- }
- h->type = NCP_REQUEST;
- /*
- * The server shouldn't know or care what task is making a
- * request, so we always use the same task number.
- */
- h->task = 2; /* (current->pid) & 0xff; */
- h->function = function;
-
- result = ncp_do_request(server, server->current_size, reply, size);
- if (result < 0) {
- ncp_dbg(1, "ncp_request_error: %d\n", result);
- goto out;
- }
- server->completion = reply->completion_code;
- server->conn_status = reply->connection_state;
- server->reply_size = result;
- server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
-
- result = reply->completion_code;
-
- if (result != 0)
- ncp_vdbg("completion code=%x\n", result);
-out:
- return result;
-}
-
-int ncp_connect(struct ncp_server *server)
-{
- struct ncp_request_header *h;
- int result;
-
- server->connection = 0xFFFF;
- server->sequence = 255;
-
- h = (struct ncp_request_header *) (server->packet);
- h->type = NCP_ALLOC_SLOT_REQUEST;
- h->task = 2; /* see above */
- h->function = 0;
-
- result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
- if (result < 0)
- goto out;
- server->connection = h->conn_low + (h->conn_high * 256);
- result = 0;
-out:
- return result;
-}
-
-int ncp_disconnect(struct ncp_server *server)
-{
- struct ncp_request_header *h;
-
- h = (struct ncp_request_header *) (server->packet);
- h->type = NCP_DEALLOC_SLOT_REQUEST;
- h->task = 2; /* see above */
- h->function = 0;
-
- return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
-}
-
-void ncp_lock_server(struct ncp_server *server)
-{
- mutex_lock(&server->mutex);
- if (server->lock)
- pr_warn("%s: was locked!\n", __func__);
- server->lock = 1;
-}
-
-void ncp_unlock_server(struct ncp_server *server)
-{
- if (!server->lock) {
- pr_warn("%s: was not locked!\n", __func__);
- return;
- }
- server->lock = 0;
- mutex_unlock(&server->mutex);
-}
diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c
deleted file mode 100644
index b6e16da4837a..000000000000
--- a/fs/ncpfs/symlink.c
+++ /dev/null
@@ -1,182 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/fs/ncpfs/symlink.c
- *
- * Code for allowing symbolic links on NCPFS (i.e. NetWare)
- * Symbolic links are not supported on native NetWare, so we use an
- * infrequently-used flag (Sh) and store a two-word magic header in
- * the file to make sure we don't accidentally use a non-link file
- * as a link.
- *
- * When using the NFS namespace, we set the mode to indicate a symlink and
- * don't bother with the magic numbers.
- *
- * from linux/fs/ext2/symlink.c
- *
- * Copyright (C) 1998-99, Frank A. Vorstenbosch
- *
- * ncpfs symlink handling code
- * NLS support (c) 1999 Petr Vandrovec
- * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
- *
- */
-
-
-#include <linux/uaccess.h>
-
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/time.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/stat.h>
-#include "ncp_fs.h"
-
-/* these magic numbers must appear in the symlink file -- this makes it a bit
- more resilient against the magic attributes being set on random files. */
-
-#define NCP_SYMLINK_MAGIC0 cpu_to_le32(0x6c6d7973) /* "symlnk->" */
-#define NCP_SYMLINK_MAGIC1 cpu_to_le32(0x3e2d6b6e)
-
-/* ----- read a symbolic link ------------------------------------------ */
-
-static int ncp_symlink_readpage(struct file *file, struct page *page)
-{
- struct inode *inode = page->mapping->host;
- int error, length, len;
- char *link, *rawlink;
- char *buf = kmap(page);
-
- error = -ENOMEM;
- rawlink = kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL);
- if (!rawlink)
- goto fail;
-
- if (ncp_make_open(inode,O_RDONLY))
- goto failEIO;
-
- error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle,
- 0,NCP_MAX_SYMLINK_SIZE,rawlink,&length);
-
- ncp_inode_close(inode);
- /* Close file handle if no other users... */
- ncp_make_closed(inode);
- if (error)
- goto failEIO;
-
- if (NCP_FINFO(inode)->flags & NCPI_KLUDGE_SYMLINK) {
- if (length<NCP_MIN_SYMLINK_SIZE ||
- ((__le32 *)rawlink)[0]!=NCP_SYMLINK_MAGIC0 ||
- ((__le32 *)rawlink)[1]!=NCP_SYMLINK_MAGIC1)
- goto failEIO;
- link = rawlink + 8;
- length -= 8;
- } else {
- link = rawlink;
- }
-
- len = NCP_MAX_SYMLINK_SIZE;
- error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link, length, 0);
- kfree(rawlink);
- if (error)
- goto fail;
- SetPageUptodate(page);
- kunmap(page);
- unlock_page(page);
- return 0;
-
-failEIO:
- error = -EIO;
- kfree(rawlink);
-fail:
- SetPageError(page);
- kunmap(page);
- unlock_page(page);
- return error;
-}
-
-/*
- * symlinks can't do much...
- */
-const struct address_space_operations ncp_symlink_aops = {
- .readpage = ncp_symlink_readpage,
-};
-
-/* ----- create a new symbolic link -------------------------------------- */
-
-int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) {
- struct inode *inode;
- char *rawlink;
- int length, err, i, outlen;
- int kludge;
- umode_t mode;
- __le32 attr;
- unsigned int hdr;
-
- ncp_dbg(1, "dir=%p, dentry=%p, symname=%s\n", dir, dentry, symname);
-
- if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber))
- kludge = 0;
- else
-#ifdef CONFIG_NCPFS_EXTRAS
- if (NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS)
- kludge = 1;
- else
-#endif
- /* EPERM is returned by VFS if symlink procedure does not exist */
- return -EPERM;
-
- rawlink = kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL);
- if (!rawlink)
- return -ENOMEM;
-
- if (kludge) {
- mode = 0;
- attr = aSHARED | aHIDDEN;
- ((__le32 *)rawlink)[0]=NCP_SYMLINK_MAGIC0;
- ((__le32 *)rawlink)[1]=NCP_SYMLINK_MAGIC1;
- hdr = 8;
- } else {
- mode = S_IFLNK | S_IRWXUGO;
- attr = 0;
- hdr = 0;
- }
-
- length = strlen(symname);
- /* map to/from server charset, do not touch upper/lower case as
- symlink can point out of ncp filesystem */
- outlen = NCP_MAX_SYMLINK_SIZE - hdr;
- err = ncp_io2vol(NCP_SERVER(dir), rawlink + hdr, &outlen, symname, length, 0);
- if (err)
- goto failfree;
-
- outlen += hdr;
-
- err = -EIO;
- if (ncp_create_new(dir,dentry,mode,0,attr)) {
- goto failfree;
- }
-
- inode=d_inode(dentry);
-
- if (ncp_make_open(inode, O_WRONLY))
- goto failfree;
-
- if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
- 0, outlen, rawlink, &i) || i!=outlen) {
- goto fail;
- }
-
- ncp_inode_close(inode);
- ncp_make_closed(inode);
- kfree(rawlink);
- return 0;
-fail:;
- ncp_inode_close(inode);
- ncp_make_closed(inode);
-failfree:;
- kfree(rawlink);
- return err;
-}
-
-/* ----- EOF ----- */
diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c
index 6c5009cc4e6f..68cb9e4740b4 100644
--- a/fs/nilfs2/segbuf.c
+++ b/fs/nilfs2/segbuf.c
@@ -130,7 +130,7 @@ int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *segbuf,
}
int nilfs_segbuf_reset(struct nilfs_segment_buffer *segbuf, unsigned int flags,
- time_t ctime, __u64 cno)
+ time64_t ctime, __u64 cno)
{
int err;
diff --git a/fs/nilfs2/segbuf.h b/fs/nilfs2/segbuf.h
index 7bbccc099709..10e16935fff6 100644
--- a/fs/nilfs2/segbuf.h
+++ b/fs/nilfs2/segbuf.h
@@ -46,7 +46,7 @@ struct nilfs_segsum_info {
unsigned long nfileblk;
u64 seg_seq;
__u64 cno;
- time_t ctime;
+ time64_t ctime;
sector_t next;
};
@@ -120,7 +120,7 @@ void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf,
struct nilfs_segment_buffer *prev);
void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *, __u64,
struct the_nilfs *);
-int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned int, time_t,
+int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned int, time64_t,
__u64);
int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *);
int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *,
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 9f3ffba41533..0953635e7d48 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -2040,7 +2040,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode)
goto out;
/* Update time stamp */
- sci->sc_seg_ctime = get_seconds();
+ sci->sc_seg_ctime = ktime_get_real_seconds();
err = nilfs_segctor_collect(sci, nilfs, mode);
if (unlikely(err))
diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h
index 84084a4d9b3e..04634e3e3d58 100644
--- a/fs/nilfs2/segment.h
+++ b/fs/nilfs2/segment.h
@@ -157,7 +157,7 @@ struct nilfs_sc_info {
unsigned long sc_blk_cnt;
unsigned long sc_datablk_cnt;
unsigned long sc_nblk_this_inc;
- time_t sc_seg_ctime;
+ time64_t sc_seg_ctime;
__u64 sc_cno;
unsigned long sc_flags;
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
index 1341a41e7b43..c7fa139d50e8 100644
--- a/fs/nilfs2/sufile.c
+++ b/fs/nilfs2/sufile.c
@@ -526,7 +526,7 @@ int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum)
* @modtime: modification time (option)
*/
int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
- unsigned long nblocks, time_t modtime)
+ unsigned long nblocks, time64_t modtime)
{
struct buffer_head *bh;
struct nilfs_segment_usage *su;
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index 158a9190c8ec..673a891350f4 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -35,7 +35,7 @@ int nilfs_sufile_set_alloc_range(struct inode *sufile, __u64 start, __u64 end);
int nilfs_sufile_alloc(struct inode *, __u64 *);
int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum);
int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
- unsigned long nblocks, time_t modtime);
+ unsigned long nblocks, time64_t modtime);
int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned int,
size_t);
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 3073b646e1ba..6ffeca84d7c3 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -283,10 +283,10 @@ int nilfs_commit_super(struct super_block *sb, int flag)
{
struct the_nilfs *nilfs = sb->s_fs_info;
struct nilfs_super_block **sbp = nilfs->ns_sbp;
- time_t t;
+ time64_t t;
/* nilfs->ns_sem must be locked by the caller. */
- t = get_seconds();
+ t = ktime_get_real_seconds();
nilfs->ns_sbwtime = t;
sbp[0]->s_wtime = cpu_to_le64(t);
sbp[0]->s_sum = 0;
diff --git a/fs/nilfs2/sysfs.c b/fs/nilfs2/sysfs.c
index 490303e3d517..4b25837e7724 100644
--- a/fs/nilfs2/sysfs.c
+++ b/fs/nilfs2/sysfs.c
@@ -31,7 +31,7 @@ static struct kset *nilfs_kset;
#define NILFS_SHOW_TIME(time_t_val, buf) ({ \
struct tm res; \
int count = 0; \
- time_to_tm(time_t_val, 0, &res); \
+ time64_to_tm(time_t_val, 0, &res); \
res.tm_year += 1900; \
res.tm_mon += 1; \
count = scnprintf(buf, PAGE_SIZE, \
@@ -579,7 +579,7 @@ nilfs_segctor_last_seg_write_time_show(struct nilfs_segctor_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t ctime;
+ time64_t ctime;
down_read(&nilfs->ns_segctor_sem);
ctime = nilfs->ns_ctime;
@@ -593,13 +593,13 @@ nilfs_segctor_last_seg_write_time_secs_show(struct nilfs_segctor_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t ctime;
+ time64_t ctime;
down_read(&nilfs->ns_segctor_sem);
ctime = nilfs->ns_ctime;
up_read(&nilfs->ns_segctor_sem);
- return snprintf(buf, PAGE_SIZE, "%llu\n", (unsigned long long)ctime);
+ return snprintf(buf, PAGE_SIZE, "%llu\n", ctime);
}
static ssize_t
@@ -607,7 +607,7 @@ nilfs_segctor_last_nongc_write_time_show(struct nilfs_segctor_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t nongc_ctime;
+ time64_t nongc_ctime;
down_read(&nilfs->ns_segctor_sem);
nongc_ctime = nilfs->ns_nongc_ctime;
@@ -621,14 +621,13 @@ nilfs_segctor_last_nongc_write_time_secs_show(struct nilfs_segctor_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t nongc_ctime;
+ time64_t nongc_ctime;
down_read(&nilfs->ns_segctor_sem);
nongc_ctime = nilfs->ns_nongc_ctime;
up_read(&nilfs->ns_segctor_sem);
- return snprintf(buf, PAGE_SIZE, "%llu\n",
- (unsigned long long)nongc_ctime);
+ return snprintf(buf, PAGE_SIZE, "%llu\n", nongc_ctime);
}
static ssize_t
@@ -728,7 +727,7 @@ nilfs_superblock_sb_write_time_show(struct nilfs_superblock_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t sbwtime;
+ time64_t sbwtime;
down_read(&nilfs->ns_sem);
sbwtime = nilfs->ns_sbwtime;
@@ -742,13 +741,13 @@ nilfs_superblock_sb_write_time_secs_show(struct nilfs_superblock_attr *attr,
struct the_nilfs *nilfs,
char *buf)
{
- time_t sbwtime;
+ time64_t sbwtime;
down_read(&nilfs->ns_sem);
sbwtime = nilfs->ns_sbwtime;
up_read(&nilfs->ns_sem);
- return snprintf(buf, PAGE_SIZE, "%llu\n", (unsigned long long)sbwtime);
+ return snprintf(buf, PAGE_SIZE, "%llu\n", sbwtime);
}
static ssize_t
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 883d732b0259..36da1779f976 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -116,7 +116,7 @@ struct the_nilfs {
*/
struct buffer_head *ns_sbh[2];
struct nilfs_super_block *ns_sbp[2];
- time_t ns_sbwtime;
+ time64_t ns_sbwtime;
unsigned int ns_sbwcount;
unsigned int ns_sbsize;
unsigned int ns_mount_state;
@@ -131,8 +131,8 @@ struct the_nilfs {
__u64 ns_nextnum;
unsigned long ns_pseg_offset;
__u64 ns_cno;
- time_t ns_ctime;
- time_t ns_nongc_ctime;
+ time64_t ns_ctime;
+ time64_t ns_nongc_ctime;
atomic_t ns_ndirtyblks;
/*
@@ -267,7 +267,7 @@ struct nilfs_root {
static inline int nilfs_sb_need_update(struct the_nilfs *nilfs)
{
- u64 t = get_seconds();
+ u64 t = ktime_get_real_seconds();
return t < nilfs->ns_sbwtime ||
t > nilfs->ns_sbwtime + nilfs->ns_sb_update_freq;
diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index 36f1390b5ed7..62d49e53061c 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -610,11 +610,16 @@ void orangefs_kill_sb(struct super_block *sb)
int orangefs_inode_cache_initialize(void)
{
- orangefs_inode_cache = kmem_cache_create("orangefs_inode_cache",
- sizeof(struct orangefs_inode_s),
- 0,
- ORANGEFS_CACHE_CREATE_FLAGS,
- orangefs_inode_cache_ctor);
+ orangefs_inode_cache = kmem_cache_create_usercopy(
+ "orangefs_inode_cache",
+ sizeof(struct orangefs_inode_s),
+ 0,
+ ORANGEFS_CACHE_CREATE_FLAGS,
+ offsetof(struct orangefs_inode_s,
+ link_target),
+ sizeof_field(struct orangefs_inode_s,
+ link_target),
+ orangefs_inode_cache_ctor);
if (!orangefs_inode_cache) {
gossip_err("Cannot create orangefs_inode_cache\n");
diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig
index 5ac415466861..406e72de88f6 100644
--- a/fs/overlayfs/Kconfig
+++ b/fs/overlayfs/Kconfig
@@ -47,9 +47,28 @@ config OVERLAY_FS_INDEX
The inodes index feature prevents breaking of lower hardlinks on copy
up.
- Note, that the inodes index feature is read-only backward compatible.
- That is, mounting an overlay which has an index dir on a kernel that
- doesn't support this feature read-only, will not have any negative
- outcomes. However, mounting the same overlay with an old kernel
- read-write and then mounting it again with a new kernel, will have
- unexpected results.
+ Note, that the inodes index feature is not backward compatible.
+ That is, mounting an overlay which has an inodes index on a kernel
+ that doesn't support this feature will have unexpected results.
+
+config OVERLAY_FS_NFS_EXPORT
+ bool "Overlayfs: turn on NFS export feature by default"
+ depends on OVERLAY_FS
+ depends on OVERLAY_FS_INDEX
+ help
+ If this config option is enabled then overlay filesystems will use
+ the inodes index dir to decode overlay NFS file handles by default.
+ In this case, it is still possible to turn off NFS export support
+ globally with the "nfs_export=off" module option or on a filesystem
+ instance basis with the "nfs_export=off" mount option.
+
+ The NFS export feature creates an index on copy up of every file and
+ directory. This full index is used to detect overlay filesystems
+ inconsistencies on lookup, like redirect from multiple upper dirs to
+ the same lower dir. The full index may incur some overhead on mount
+ time, especially when verifying that directory file handles are not
+ stale.
+
+ Note, that the NFS export feature is not backward compatible.
+ That is, mounting an overlay which has a full index on a kernel
+ that doesn't support this feature will have unexpected results.
diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile
index 99373bbc1478..30802347a020 100644
--- a/fs/overlayfs/Makefile
+++ b/fs/overlayfs/Makefile
@@ -4,4 +4,5 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o
-overlay-objs := super.o namei.o util.o inode.o dir.o readdir.o copy_up.o
+overlay-objs := super.o namei.o util.o inode.o dir.o readdir.o copy_up.o \
+ export.o
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index eb3b8d39fb61..d855f508fa20 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -232,13 +232,13 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
return err;
}
-struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper)
+struct ovl_fh *ovl_encode_fh(struct dentry *real, bool is_upper)
{
struct ovl_fh *fh;
int fh_type, fh_len, dwords;
void *buf;
int buflen = MAX_HANDLE_SZ;
- uuid_t *uuid = &lower->d_sb->s_uuid;
+ uuid_t *uuid = &real->d_sb->s_uuid;
buf = kmalloc(buflen, GFP_KERNEL);
if (!buf)
@@ -250,7 +250,7 @@ struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper)
* the price or reconnecting the dentry.
*/
dwords = buflen >> 2;
- fh_type = exportfs_encode_fh(lower, buf, &dwords, 0);
+ fh_type = exportfs_encode_fh(real, buf, &dwords, 0);
buflen = (dwords << 2);
fh = ERR_PTR(-EIO);
@@ -288,8 +288,8 @@ out:
return fh;
}
-static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
- struct dentry *upper)
+int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
+ struct dentry *upper)
{
const struct ovl_fh *fh = NULL;
int err;
@@ -315,6 +315,94 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
return err;
}
+/* Store file handle of @upper dir in @index dir entry */
+static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
+{
+ const struct ovl_fh *fh;
+ int err;
+
+ fh = ovl_encode_fh(upper, true);
+ if (IS_ERR(fh))
+ return PTR_ERR(fh);
+
+ err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh, fh->len, 0);
+
+ kfree(fh);
+ return err;
+}
+
+/*
+ * Create and install index entry.
+ *
+ * Caller must hold i_mutex on indexdir.
+ */
+static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
+ struct dentry *upper)
+{
+ struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
+ struct inode *dir = d_inode(indexdir);
+ struct dentry *index = NULL;
+ struct dentry *temp = NULL;
+ struct qstr name = { };
+ int err;
+
+ /*
+ * For now this is only used for creating index entry for directories,
+ * because non-dir are copied up directly to index and then hardlinked
+ * to upper dir.
+ *
+ * TODO: implement create index for non-dir, so we can call it when
+ * encoding file handle for non-dir in case index does not exist.
+ */
+ if (WARN_ON(!d_is_dir(dentry)))
+ return -EIO;
+
+ /* Directory not expected to be indexed before copy up */
+ if (WARN_ON(ovl_test_flag(OVL_INDEX, d_inode(dentry))))
+ return -EIO;
+
+ err = ovl_get_index_name(origin, &name);
+ if (err)
+ return err;
+
+ temp = ovl_lookup_temp(indexdir);
+ if (IS_ERR(temp))
+ goto temp_err;
+
+ err = ovl_do_mkdir(dir, temp, S_IFDIR, true);
+ if (err)
+ goto out;
+
+ err = ovl_set_upper_fh(upper, temp);
+ if (err)
+ goto out_cleanup;
+
+ index = lookup_one_len(name.name, indexdir, name.len);
+ if (IS_ERR(index)) {
+ err = PTR_ERR(index);
+ } else {
+ err = ovl_do_rename(dir, temp, dir, index, 0);
+ dput(index);
+ }
+
+ if (err)
+ goto out_cleanup;
+
+out:
+ dput(temp);
+ kfree(name.name);
+ return err;
+
+temp_err:
+ err = PTR_ERR(temp);
+ temp = NULL;
+ goto out;
+
+out_cleanup:
+ ovl_cleanup(dir, temp);
+ goto out;
+}
+
struct ovl_copy_up_ctx {
struct dentry *parent;
struct dentry *dentry;
@@ -327,6 +415,7 @@ struct ovl_copy_up_ctx {
struct dentry *workdir;
bool tmpfile;
bool origin;
+ bool indexed;
};
static int ovl_link_up(struct ovl_copy_up_ctx *c)
@@ -361,7 +450,10 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
}
}
inode_unlock(udir);
- ovl_set_nlink_upper(c->dentry);
+ if (err)
+ return err;
+
+ err = ovl_set_nlink_upper(c->dentry);
return err;
}
@@ -498,6 +590,12 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
if (err)
goto out_cleanup;
+ if (S_ISDIR(c->stat.mode) && c->indexed) {
+ err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
+ if (err)
+ goto out_cleanup;
+ }
+
if (c->tmpfile) {
inode_lock_nested(udir, I_MUTEX_PARENT);
err = ovl_install_temp(c, temp, &newdentry);
@@ -536,20 +634,33 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
{
int err;
struct ovl_fs *ofs = c->dentry->d_sb->s_fs_info;
- bool indexed = false;
+ bool to_index = false;
- if (ovl_indexdir(c->dentry->d_sb) && !S_ISDIR(c->stat.mode) &&
- c->stat.nlink > 1)
- indexed = true;
+ /*
+ * Indexed non-dir is copied up directly to the index entry and then
+ * hardlinked to upper dir. Indexed dir is copied up to indexdir,
+ * then index entry is created and then copied up dir installed.
+ * Copying dir up to indexdir instead of workdir simplifies locking.
+ */
+ if (ovl_need_index(c->dentry)) {
+ c->indexed = true;
+ if (S_ISDIR(c->stat.mode))
+ c->workdir = ovl_indexdir(c->dentry->d_sb);
+ else
+ to_index = true;
+ }
- if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || indexed)
+ if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || to_index)
c->origin = true;
- if (indexed) {
+ if (to_index) {
c->destdir = ovl_indexdir(c->dentry->d_sb);
err = ovl_get_index_name(c->lowerpath.dentry, &c->destname);
if (err)
return err;
+ } else if (WARN_ON(!c->parent)) {
+ /* Disconnected dentry must be copied up to index dir */
+ return -EIO;
} else {
/*
* Mark parent "impure" because it may now contain non-pure
@@ -572,11 +683,17 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
}
}
- if (indexed) {
- if (!err)
- ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
- kfree(c->destname.name);
- } else if (!err) {
+
+ if (err)
+ goto out;
+
+ if (c->indexed)
+ ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
+
+ if (to_index) {
+ /* Initialize nlink for copy up of disconnected dentry */
+ err = ovl_set_nlink_upper(c->dentry);
+ } else {
struct inode *udir = d_inode(c->destdir);
/* Restore timestamps on parent (best effort) */
@@ -587,6 +704,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
ovl_dentry_set_upper_alias(c->dentry);
}
+out:
+ if (to_index)
+ kfree(c->destname.name);
return err;
}
@@ -611,14 +731,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err)
return err;
- ovl_path_upper(parent, &parentpath);
- ctx.destdir = parentpath.dentry;
- ctx.destname = dentry->d_name;
+ if (parent) {
+ ovl_path_upper(parent, &parentpath);
+ ctx.destdir = parentpath.dentry;
+ ctx.destname = dentry->d_name;
- err = vfs_getattr(&parentpath, &ctx.pstat,
- STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT);
- if (err)
- return err;
+ err = vfs_getattr(&parentpath, &ctx.pstat,
+ STATX_ATIME | STATX_MTIME,
+ AT_STATX_SYNC_AS_STAT);
+ if (err)
+ return err;
+ }
/* maybe truncate regular file. this has no effect on dirs */
if (flags & O_TRUNC)
@@ -639,7 +762,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
} else {
if (!ovl_dentry_upper(dentry))
err = ovl_do_copy_up(&ctx);
- if (!err && !ovl_dentry_has_upper_alias(dentry))
+ if (!err && parent && !ovl_dentry_has_upper_alias(dentry))
err = ovl_link_up(&ctx);
ovl_copy_up_end(dentry);
}
@@ -652,10 +775,19 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
{
int err = 0;
const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
+ bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED);
+
+ /*
+ * With NFS export, copy up can get called for a disconnected non-dir.
+ * In this case, we will copy up lower inode to index dir without
+ * linking it to upper dir.
+ */
+ if (WARN_ON(disconnected && d_is_dir(dentry)))
+ return -EIO;
while (!err) {
struct dentry *next;
- struct dentry *parent;
+ struct dentry *parent = NULL;
/*
* Check if copy-up has happened as well as for upper alias (in
@@ -671,12 +803,12 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
* with rename.
*/
if (ovl_dentry_upper(dentry) &&
- ovl_dentry_has_upper_alias(dentry))
+ (ovl_dentry_has_upper_alias(dentry) || disconnected))
break;
next = dget(dentry);
/* find the topmost dentry not yet copied up */
- for (;;) {
+ for (; !disconnected;) {
parent = dget_parent(next);
if (ovl_dentry_upper(parent))
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index f9788bc116a8..839709c7803a 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -63,8 +63,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
}
/* caller holds i_mutex on workdir */
-static struct dentry *ovl_whiteout(struct dentry *workdir,
- struct dentry *dentry)
+static struct dentry *ovl_whiteout(struct dentry *workdir)
{
int err;
struct dentry *whiteout;
@@ -83,6 +82,38 @@ static struct dentry *ovl_whiteout(struct dentry *workdir,
return whiteout;
}
+/* Caller must hold i_mutex on both workdir and dir */
+int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *wdir = workdir->d_inode;
+ struct dentry *whiteout;
+ int err;
+ int flags = 0;
+
+ whiteout = ovl_whiteout(workdir);
+ err = PTR_ERR(whiteout);
+ if (IS_ERR(whiteout))
+ return err;
+
+ if (d_is_dir(dentry))
+ flags = RENAME_EXCHANGE;
+
+ err = ovl_do_rename(wdir, whiteout, dir, dentry, flags);
+ if (err)
+ goto kill_whiteout;
+ if (flags)
+ ovl_cleanup(wdir, dentry);
+
+out:
+ dput(whiteout);
+ return err;
+
+kill_whiteout:
+ ovl_cleanup(wdir, whiteout);
+ goto out;
+}
+
int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr, struct dentry *hardlink, bool debug)
{
@@ -181,11 +212,6 @@ static bool ovl_type_origin(struct dentry *dentry)
return OVL_TYPE_ORIGIN(ovl_path_type(dentry));
}
-static bool ovl_may_have_whiteouts(struct dentry *dentry)
-{
- return ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
-}
-
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct cattr *attr, struct dentry *hardlink)
{
@@ -301,37 +327,6 @@ out:
return ERR_PTR(err);
}
-static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
-{
- int err;
- struct dentry *ret = NULL;
- LIST_HEAD(list);
-
- err = ovl_check_empty_dir(dentry, &list);
- if (err) {
- ret = ERR_PTR(err);
- goto out_free;
- }
-
- /*
- * When removing an empty opaque directory, then it makes no sense to
- * replace it with an exact replica of itself.
- *
- * If upperdentry has whiteouts, clear them.
- *
- * Can race with copy-up, since we don't hold the upperdir mutex.
- * Doesn't matter, since copy-up can't create a non-empty directory
- * from an empty one.
- */
- if (!list_empty(&list))
- ret = ovl_clear_empty(dentry, &list);
-
-out_free:
- ovl_cache_free(&list);
-
- return ret;
-}
-
static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
const struct posix_acl *acl)
{
@@ -623,23 +618,20 @@ static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper)
return d_inode(ovl_dentry_upper(dentry)) == d_inode(upper);
}
-static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
+static int ovl_remove_and_whiteout(struct dentry *dentry,
+ struct list_head *list)
{
struct dentry *workdir = ovl_workdir(dentry);
- struct inode *wdir = workdir->d_inode;
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
- struct inode *udir = upperdir->d_inode;
- struct dentry *whiteout;
struct dentry *upper;
struct dentry *opaquedir = NULL;
int err;
- int flags = 0;
if (WARN_ON(!workdir))
return -EROFS;
- if (is_dir) {
- opaquedir = ovl_check_empty_and_clear(dentry);
+ if (!list_empty(list)) {
+ opaquedir = ovl_clear_empty(dentry, list);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
goto out;
@@ -662,24 +654,13 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
goto out_dput_upper;
}
- whiteout = ovl_whiteout(workdir, dentry);
- err = PTR_ERR(whiteout);
- if (IS_ERR(whiteout))
- goto out_dput_upper;
-
- if (d_is_dir(upper))
- flags = RENAME_EXCHANGE;
-
- err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+ err = ovl_cleanup_and_whiteout(workdir, d_inode(upperdir), upper);
if (err)
- goto kill_whiteout;
- if (flags)
- ovl_cleanup(wdir, upper);
+ goto out_d_drop;
ovl_dentry_version_inc(dentry->d_parent, true);
out_d_drop:
d_drop(dentry);
- dput(whiteout);
out_dput_upper:
dput(upper);
out_unlock:
@@ -688,13 +669,10 @@ out_dput:
dput(opaquedir);
out:
return err;
-
-kill_whiteout:
- ovl_cleanup(wdir, whiteout);
- goto out_d_drop;
}
-static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
+static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
+ struct list_head *list)
{
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *dir = upperdir->d_inode;
@@ -702,10 +680,8 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
struct dentry *opaquedir = NULL;
int err;
- /* Redirect/origin dir can be !ovl_lower_positive && not clean */
- if (is_dir && (ovl_dentry_get_redirect(dentry) ||
- ovl_may_have_whiteouts(dentry))) {
- opaquedir = ovl_check_empty_and_clear(dentry);
+ if (!list_empty(list)) {
+ opaquedir = ovl_clear_empty(dentry, list);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
goto out;
@@ -746,11 +722,26 @@ out:
return err;
}
+static bool ovl_pure_upper(struct dentry *dentry)
+{
+ return !ovl_dentry_lower(dentry) &&
+ !ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
+}
+
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{
int err;
bool locked = false;
const struct cred *old_cred;
+ bool lower_positive = ovl_lower_positive(dentry);
+ LIST_HEAD(list);
+
+ /* No need to clean pure upper removed by vfs_rmdir() */
+ if (is_dir && (lower_positive || !ovl_pure_upper(dentry))) {
+ err = ovl_check_empty_dir(dentry, &list);
+ if (err)
+ goto out;
+ }
err = ovl_want_write(dentry);
if (err)
@@ -765,10 +756,10 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out_drop_write;
old_cred = ovl_override_creds(dentry->d_sb);
- if (!ovl_lower_positive(dentry))
- err = ovl_remove_upper(dentry, is_dir);
+ if (!lower_positive)
+ err = ovl_remove_upper(dentry, is_dir, &list);
else
- err = ovl_remove_and_whiteout(dentry, is_dir);
+ err = ovl_remove_and_whiteout(dentry, &list);
revert_creds(old_cred);
if (!err) {
if (is_dir)
@@ -780,6 +771,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
out_drop_write:
ovl_drop_write(dentry);
out:
+ ovl_cache_free(&list);
return err;
}
@@ -915,6 +907,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
bool samedir = olddir == newdir;
struct dentry *opaquedir = NULL;
const struct cred *old_cred = NULL;
+ LIST_HEAD(list);
err = -EINVAL;
if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
@@ -929,6 +922,27 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
if (!overwrite && !ovl_can_move(new))
goto out;
+ if (overwrite && new_is_dir && !ovl_pure_upper(new)) {
+ err = ovl_check_empty_dir(new, &list);
+ if (err)
+ goto out;
+ }
+
+ if (overwrite) {
+ if (ovl_lower_positive(old)) {
+ if (!ovl_dentry_is_whiteout(new)) {
+ /* Whiteout source */
+ flags |= RENAME_WHITEOUT;
+ } else {
+ /* Switch whiteouts */
+ flags |= RENAME_EXCHANGE;
+ }
+ } else if (is_dir && ovl_dentry_is_whiteout(new)) {
+ flags |= RENAME_EXCHANGE;
+ cleanup_whiteout = true;
+ }
+ }
+
err = ovl_want_write(old);
if (err)
goto out;
@@ -952,9 +966,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
old_cred = ovl_override_creds(old->d_sb);
- if (overwrite && new_is_dir && (ovl_type_merge_or_lower(new) ||
- ovl_may_have_whiteouts(new))) {
- opaquedir = ovl_check_empty_and_clear(new);
+ if (!list_empty(&list)) {
+ opaquedir = ovl_clear_empty(new, &list);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) {
opaquedir = NULL;
@@ -962,21 +975,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
}
}
- if (overwrite) {
- if (ovl_lower_positive(old)) {
- if (!ovl_dentry_is_whiteout(new)) {
- /* Whiteout source */
- flags |= RENAME_WHITEOUT;
- } else {
- /* Switch whiteouts */
- flags |= RENAME_EXCHANGE;
- }
- } else if (is_dir && ovl_dentry_is_whiteout(new)) {
- flags |= RENAME_EXCHANGE;
- cleanup_whiteout = true;
- }
- }
-
old_upperdir = ovl_dentry_upper(old->d_parent);
new_upperdir = ovl_dentry_upper(new->d_parent);
@@ -1094,6 +1092,7 @@ out_drop_write:
ovl_drop_write(old);
out:
dput(opaquedir);
+ ovl_cache_free(&list);
return err;
}
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
new file mode 100644
index 000000000000..bb94ce9da5c8
--- /dev/null
+++ b/fs/overlayfs/export.c
@@ -0,0 +1,715 @@
+/*
+ * Overlayfs NFS export support.
+ *
+ * Amir Goldstein <amir73il@gmail.com>
+ *
+ * Copyright (C) 2017-2018 CTERA Networks. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/cred.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/xattr.h>
+#include <linux/exportfs.h>
+#include <linux/ratelimit.h>
+#include "overlayfs.h"
+
+/*
+ * We only need to encode origin if there is a chance that the same object was
+ * encoded pre copy up and then we need to stay consistent with the same
+ * encoding also after copy up. If non-pure upper is not indexed, then it was
+ * copied up before NFS export was enabled. In that case we don't need to worry
+ * about staying consistent with pre copy up encoding and we encode an upper
+ * file handle. Overlay root dentry is a private case of non-indexed upper.
+ *
+ * The following table summarizes the different file handle encodings used for
+ * different overlay object types:
+ *
+ * Object type | Encoding
+ * --------------------------------
+ * Pure upper | U
+ * Non-indexed upper | U
+ * Indexed upper | L (*)
+ * Non-upper | L (*)
+ *
+ * U = upper file handle
+ * L = lower file handle
+ *
+ * (*) Connecting an overlay dir from real lower dentry is not always
+ * possible when there are redirects in lower layers. To mitigate this case,
+ * we copy up the lower dir first and then encode an upper dir file handle.
+ */
+static bool ovl_should_encode_origin(struct dentry *dentry)
+{
+ struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+
+ if (!ovl_dentry_lower(dentry))
+ return false;
+
+ /*
+ * Decoding a merge dir, whose origin's parent is under a redirected
+ * lower dir is not always possible. As a simple aproximation, we do
+ * not encode lower dir file handles when overlay has multiple lower
+ * layers and origin is below the topmost lower layer.
+ *
+ * TODO: copy up only the parent that is under redirected lower.
+ */
+ if (d_is_dir(dentry) && ofs->upper_mnt &&
+ OVL_E(dentry)->lowerstack[0].layer->idx > 1)
+ return false;
+
+ /* Decoding a non-indexed upper from origin is not implemented */
+ if (ovl_dentry_upper(dentry) &&
+ !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
+ return false;
+
+ return true;
+}
+
+static int ovl_encode_maybe_copy_up(struct dentry *dentry)
+{
+ int err;
+
+ if (ovl_dentry_upper(dentry))
+ return 0;
+
+ err = ovl_want_write(dentry);
+ if (err)
+ return err;
+
+ err = ovl_copy_up(dentry);
+
+ ovl_drop_write(dentry);
+ return err;
+}
+
+static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
+{
+ struct dentry *origin = ovl_dentry_lower(dentry);
+ struct ovl_fh *fh = NULL;
+ int err;
+
+ /*
+ * If we should not encode a lower dir file handle, copy up and encode
+ * an upper dir file handle.
+ */
+ if (!ovl_should_encode_origin(dentry)) {
+ err = ovl_encode_maybe_copy_up(dentry);
+ if (err)
+ goto fail;
+
+ origin = NULL;
+ }
+
+ /* Encode an upper or origin file handle */
+ fh = ovl_encode_fh(origin ?: ovl_dentry_upper(dentry), !origin);
+ err = PTR_ERR(fh);
+ if (IS_ERR(fh))
+ goto fail;
+
+ err = -EOVERFLOW;
+ if (fh->len > buflen)
+ goto fail;
+
+ memcpy(buf, (char *)fh, fh->len);
+ err = fh->len;
+
+out:
+ kfree(fh);
+ return err;
+
+fail:
+ pr_warn_ratelimited("overlayfs: failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n",
+ dentry, err, buflen, fh ? (int)fh->len : 0,
+ fh ? fh->type : 0);
+ goto out;
+}
+
+static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len)
+{
+ int res, len = *max_len << 2;
+
+ res = ovl_d_to_fh(dentry, (char *)fid, len);
+ if (res <= 0)
+ return FILEID_INVALID;
+
+ len = res;
+
+ /* Round up to dwords */
+ *max_len = (len + 3) >> 2;
+ return OVL_FILEID;
+}
+
+static int ovl_encode_inode_fh(struct inode *inode, u32 *fid, int *max_len,
+ struct inode *parent)
+{
+ struct dentry *dentry;
+ int type;
+
+ /* TODO: encode connectable file handles */
+ if (parent)
+ return FILEID_INVALID;
+
+ dentry = d_find_any_alias(inode);
+ if (WARN_ON(!dentry))
+ return FILEID_INVALID;
+
+ type = ovl_dentry_to_fh(dentry, fid, max_len);
+
+ dput(dentry);
+ return type;
+}
+
+/*
+ * Find or instantiate an overlay dentry from real dentries and index.
+ */
+static struct dentry *ovl_obtain_alias(struct super_block *sb,
+ struct dentry *upper_alias,
+ struct ovl_path *lowerpath,
+ struct dentry *index)
+{
+ struct dentry *lower = lowerpath ? lowerpath->dentry : NULL;
+ struct dentry *upper = upper_alias ?: index;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct ovl_entry *oe;
+
+ /* We get overlay directory dentries with ovl_lookup_real() */
+ if (d_is_dir(upper ?: lower))
+ return ERR_PTR(-EIO);
+
+ inode = ovl_get_inode(sb, dget(upper), lower, index, !!lower);
+ if (IS_ERR(inode)) {
+ dput(upper);
+ return ERR_CAST(inode);
+ }
+
+ if (index)
+ ovl_set_flag(OVL_INDEX, inode);
+
+ dentry = d_find_any_alias(inode);
+ if (!dentry) {
+ dentry = d_alloc_anon(inode->i_sb);
+ if (!dentry)
+ goto nomem;
+ oe = ovl_alloc_entry(lower ? 1 : 0);
+ if (!oe)
+ goto nomem;
+
+ if (lower) {
+ oe->lowerstack->dentry = dget(lower);
+ oe->lowerstack->layer = lowerpath->layer;
+ }
+ dentry->d_fsdata = oe;
+ if (upper_alias)
+ ovl_dentry_set_upper_alias(dentry);
+ }
+
+ return d_instantiate_anon(dentry, inode);
+
+nomem:
+ iput(inode);
+ dput(dentry);
+ return ERR_PTR(-ENOMEM);
+}
+
+/* Get the upper or lower dentry in stach whose on layer @idx */
+static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx)
+{
+ struct ovl_entry *oe = dentry->d_fsdata;
+ int i;
+
+ if (!idx)
+ return ovl_dentry_upper(dentry);
+
+ for (i = 0; i < oe->numlower; i++) {
+ if (oe->lowerstack[i].layer->idx == idx)
+ return oe->lowerstack[i].dentry;
+ }
+
+ return NULL;
+}
+
+/*
+ * Lookup a child overlay dentry to get a connected overlay dentry whose real
+ * dentry is @real. If @real is on upper layer, we lookup a child overlay
+ * dentry with the same name as the real dentry. Otherwise, we need to consult
+ * index for lookup.
+ */
+static struct dentry *ovl_lookup_real_one(struct dentry *connected,
+ struct dentry *real,
+ struct ovl_layer *layer)
+{
+ struct inode *dir = d_inode(connected);
+ struct dentry *this, *parent = NULL;
+ struct name_snapshot name;
+ int err;
+
+ /*
+ * Lookup child overlay dentry by real name. The dir mutex protects us
+ * from racing with overlay rename. If the overlay dentry that is above
+ * real has already been moved to a parent that is not under the
+ * connected overlay dir, we return -ECHILD and restart the lookup of
+ * connected real path from the top.
+ */
+ inode_lock_nested(dir, I_MUTEX_PARENT);
+ err = -ECHILD;
+ parent = dget_parent(real);
+ if (ovl_dentry_real_at(connected, layer->idx) != parent)
+ goto fail;
+
+ /*
+ * We also need to take a snapshot of real dentry name to protect us
+ * from racing with underlying layer rename. In this case, we don't
+ * care about returning ESTALE, only from dereferencing a free name
+ * pointer because we hold no lock on the real dentry.
+ */
+ take_dentry_name_snapshot(&name, real);
+ this = lookup_one_len(name.name, connected, strlen(name.name));
+ err = PTR_ERR(this);
+ if (IS_ERR(this)) {
+ goto fail;
+ } else if (!this || !this->d_inode) {
+ dput(this);
+ err = -ENOENT;
+ goto fail;
+ } else if (ovl_dentry_real_at(this, layer->idx) != real) {
+ dput(this);
+ err = -ESTALE;
+ goto fail;
+ }
+
+out:
+ release_dentry_name_snapshot(&name);
+ dput(parent);
+ inode_unlock(dir);
+ return this;
+
+fail:
+ pr_warn_ratelimited("overlayfs: failed to lookup one by real (%pd2, layer=%d, connected=%pd2, err=%i)\n",
+ real, layer->idx, connected, err);
+ this = ERR_PTR(err);
+ goto out;
+}
+
+static struct dentry *ovl_lookup_real(struct super_block *sb,
+ struct dentry *real,
+ struct ovl_layer *layer);
+
+/*
+ * Lookup an indexed or hashed overlay dentry by real inode.
+ */
+static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
+ struct dentry *real,
+ struct ovl_layer *layer)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+ struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt };
+ struct dentry *index = NULL;
+ struct dentry *this = NULL;
+ struct inode *inode;
+
+ /*
+ * Decoding upper dir from index is expensive, so first try to lookup
+ * overlay dentry in inode/dcache.
+ */
+ inode = ovl_lookup_inode(sb, real, !layer->idx);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+ if (inode) {
+ this = d_find_any_alias(inode);
+ iput(inode);
+ }
+
+ /*
+ * For decoded lower dir file handle, lookup index by origin to check
+ * if lower dir was copied up and and/or removed.
+ */
+ if (!this && layer->idx && ofs->indexdir && !WARN_ON(!d_is_dir(real))) {
+ index = ovl_lookup_index(ofs, NULL, real, false);
+ if (IS_ERR(index))
+ return index;
+ }
+
+ /* Get connected upper overlay dir from index */
+ if (index) {
+ struct dentry *upper = ovl_index_upper(ofs, index);
+
+ dput(index);
+ if (IS_ERR_OR_NULL(upper))
+ return upper;
+
+ /*
+ * ovl_lookup_real() in lower layer may call recursively once to
+ * ovl_lookup_real() in upper layer. The first level call walks
+ * back lower parents to the topmost indexed parent. The second
+ * recursive call walks back from indexed upper to the topmost
+ * connected/hashed upper parent (or up to root).
+ */
+ this = ovl_lookup_real(sb, upper, &upper_layer);
+ dput(upper);
+ }
+
+ if (!this)
+ return NULL;
+
+ if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) {
+ dput(this);
+ this = ERR_PTR(-EIO);
+ }
+
+ return this;
+}
+
+/*
+ * Lookup an indexed or hashed overlay dentry, whose real dentry is an
+ * ancestor of @real.
+ */
+static struct dentry *ovl_lookup_real_ancestor(struct super_block *sb,
+ struct dentry *real,
+ struct ovl_layer *layer)
+{
+ struct dentry *next, *parent = NULL;
+ struct dentry *ancestor = ERR_PTR(-EIO);
+
+ if (real == layer->mnt->mnt_root)
+ return dget(sb->s_root);
+
+ /* Find the topmost indexed or hashed ancestor */
+ next = dget(real);
+ for (;;) {
+ parent = dget_parent(next);
+
+ /*
+ * Lookup a matching overlay dentry in inode/dentry
+ * cache or in index by real inode.
+ */
+ ancestor = ovl_lookup_real_inode(sb, next, layer);
+ if (ancestor)
+ break;
+
+ if (parent == layer->mnt->mnt_root) {
+ ancestor = dget(sb->s_root);
+ break;
+ }
+
+ /*
+ * If @real has been moved out of the layer root directory,
+ * we will eventully hit the real fs root. This cannot happen
+ * by legit overlay rename, so we return error in that case.
+ */
+ if (parent == next) {
+ ancestor = ERR_PTR(-EXDEV);
+ break;
+ }
+
+ dput(next);
+ next = parent;
+ }
+
+ dput(parent);
+ dput(next);
+
+ return ancestor;
+}
+
+/*
+ * Lookup a connected overlay dentry whose real dentry is @real.
+ * If @real is on upper layer, we lookup a child overlay dentry with the same
+ * path the real dentry. Otherwise, we need to consult index for lookup.
+ */
+static struct dentry *ovl_lookup_real(struct super_block *sb,
+ struct dentry *real,
+ struct ovl_layer *layer)
+{
+ struct dentry *connected;
+ int err = 0;
+
+ connected = ovl_lookup_real_ancestor(sb, real, layer);
+ if (IS_ERR(connected))
+ return connected;
+
+ while (!err) {
+ struct dentry *next, *this;
+ struct dentry *parent = NULL;
+ struct dentry *real_connected = ovl_dentry_real_at(connected,
+ layer->idx);
+
+ if (real_connected == real)
+ break;
+
+ /* Find the topmost dentry not yet connected */
+ next = dget(real);
+ for (;;) {
+ parent = dget_parent(next);
+
+ if (parent == real_connected)
+ break;
+
+ /*
+ * If real has been moved out of 'real_connected',
+ * we will not find 'real_connected' and hit the layer
+ * root. In that case, we need to restart connecting.
+ * This game can go on forever in the worst case. We
+ * may want to consider taking s_vfs_rename_mutex if
+ * this happens more than once.
+ */
+ if (parent == layer->mnt->mnt_root) {
+ dput(connected);
+ connected = dget(sb->s_root);
+ break;
+ }
+
+ /*
+ * If real file has been moved out of the layer root
+ * directory, we will eventully hit the real fs root.
+ * This cannot happen by legit overlay rename, so we
+ * return error in that case.
+ */
+ if (parent == next) {
+ err = -EXDEV;
+ break;
+ }
+
+ dput(next);
+ next = parent;
+ }
+
+ if (!err) {
+ this = ovl_lookup_real_one(connected, next, layer);
+ if (IS_ERR(this))
+ err = PTR_ERR(this);
+
+ /*
+ * Lookup of child in overlay can fail when racing with
+ * overlay rename of child away from 'connected' parent.
+ * In this case, we need to restart the lookup from the
+ * top, because we cannot trust that 'real_connected' is
+ * still an ancestor of 'real'. There is a good chance
+ * that the renamed overlay ancestor is now in cache, so
+ * ovl_lookup_real_ancestor() will find it and we can
+ * continue to connect exactly from where lookup failed.
+ */
+ if (err == -ECHILD) {
+ this = ovl_lookup_real_ancestor(sb, real,
+ layer);
+ err = IS_ERR(this) ? PTR_ERR(this) : 0;
+ }
+ if (!err) {
+ dput(connected);
+ connected = this;
+ }
+ }
+
+ dput(parent);
+ dput(next);
+ }
+
+ if (err)
+ goto fail;
+
+ return connected;
+
+fail:
+ pr_warn_ratelimited("overlayfs: failed to lookup by real (%pd2, layer=%d, connected=%pd2, err=%i)\n",
+ real, layer->idx, connected, err);
+ dput(connected);
+ return ERR_PTR(err);
+}
+
+/*
+ * Get an overlay dentry from upper/lower real dentries and index.
+ */
+static struct dentry *ovl_get_dentry(struct super_block *sb,
+ struct dentry *upper,
+ struct ovl_path *lowerpath,
+ struct dentry *index)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+ struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt };
+ struct ovl_layer *layer = upper ? &upper_layer : lowerpath->layer;
+ struct dentry *real = upper ?: (index ?: lowerpath->dentry);
+
+ /*
+ * Obtain a disconnected overlay dentry from a non-dir real dentry
+ * and index.
+ */
+ if (!d_is_dir(real))
+ return ovl_obtain_alias(sb, upper, lowerpath, index);
+
+ /* Removed empty directory? */
+ if ((real->d_flags & DCACHE_DISCONNECTED) || d_unhashed(real))
+ return ERR_PTR(-ENOENT);
+
+ /*
+ * If real dentry is connected and hashed, get a connected overlay
+ * dentry whose real dentry is @real.
+ */
+ return ovl_lookup_real(sb, real, layer);
+}
+
+static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
+ struct ovl_fh *fh)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+ struct dentry *dentry;
+ struct dentry *upper;
+
+ if (!ofs->upper_mnt)
+ return ERR_PTR(-EACCES);
+
+ upper = ovl_decode_fh(fh, ofs->upper_mnt);
+ if (IS_ERR_OR_NULL(upper))
+ return upper;
+
+ dentry = ovl_get_dentry(sb, upper, NULL, NULL);
+ dput(upper);
+
+ return dentry;
+}
+
+static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
+ struct ovl_fh *fh)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+ struct ovl_path origin = { };
+ struct ovl_path *stack = &origin;
+ struct dentry *dentry = NULL;
+ struct dentry *index = NULL;
+ struct inode *inode = NULL;
+ bool is_deleted = false;
+ int err;
+
+ /* First lookup indexed upper by fh */
+ if (ofs->indexdir) {
+ index = ovl_get_index_fh(ofs, fh);
+ err = PTR_ERR(index);
+ if (IS_ERR(index)) {
+ if (err != -ESTALE)
+ return ERR_PTR(err);
+
+ /* Found a whiteout index - treat as deleted inode */
+ is_deleted = true;
+ index = NULL;
+ }
+ }
+
+ /* Then try to get upper dir by index */
+ if (index && d_is_dir(index)) {
+ struct dentry *upper = ovl_index_upper(ofs, index);
+
+ err = PTR_ERR(upper);
+ if (IS_ERR_OR_NULL(upper))
+ goto out_err;
+
+ dentry = ovl_get_dentry(sb, upper, NULL, NULL);
+ dput(upper);
+ goto out;
+ }
+
+ /* Then lookup origin by fh */
+ err = ovl_check_origin_fh(ofs, fh, NULL, &stack);
+ if (err) {
+ goto out_err;
+ } else if (index) {
+ err = ovl_verify_origin(index, origin.dentry, false);
+ if (err)
+ goto out_err;
+ } else if (is_deleted) {
+ /* Lookup deleted non-dir by origin inode */
+ if (!d_is_dir(origin.dentry))
+ inode = ovl_lookup_inode(sb, origin.dentry, false);
+ err = -ESTALE;
+ if (!inode || atomic_read(&inode->i_count) == 1)
+ goto out_err;
+
+ /* Deleted but still open? */
+ index = dget(ovl_i_dentry_upper(inode));
+ }
+
+ dentry = ovl_get_dentry(sb, NULL, &origin, index);
+
+out:
+ dput(origin.dentry);
+ dput(index);
+ iput(inode);
+ return dentry;
+
+out_err:
+ dentry = ERR_PTR(err);
+ goto out;
+}
+
+static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ struct dentry *dentry = NULL;
+ struct ovl_fh *fh = (struct ovl_fh *) fid;
+ int len = fh_len << 2;
+ unsigned int flags = 0;
+ int err;
+
+ err = -EINVAL;
+ if (fh_type != OVL_FILEID)
+ goto out_err;
+
+ err = ovl_check_fh_len(fh, len);
+ if (err)
+ goto out_err;
+
+ flags = fh->flags;
+ dentry = (flags & OVL_FH_FLAG_PATH_UPPER) ?
+ ovl_upper_fh_to_d(sb, fh) :
+ ovl_lower_fh_to_d(sb, fh);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry) && err != -ESTALE)
+ goto out_err;
+
+ return dentry;
+
+out_err:
+ pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n",
+ len, fh_type, flags, err);
+ return ERR_PTR(err);
+}
+
+static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ pr_warn_ratelimited("overlayfs: connectable file handles not supported; use 'no_subtree_check' exportfs option.\n");
+ return ERR_PTR(-EACCES);
+}
+
+static int ovl_get_name(struct dentry *parent, char *name,
+ struct dentry *child)
+{
+ /*
+ * ovl_fh_to_dentry() returns connected dir overlay dentries and
+ * ovl_fh_to_parent() is not implemented, so we should not get here.
+ */
+ WARN_ON_ONCE(1);
+ return -EIO;
+}
+
+static struct dentry *ovl_get_parent(struct dentry *dentry)
+{
+ /*
+ * ovl_fh_to_dentry() returns connected dir overlay dentries, so we
+ * should not get here.
+ */
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EIO);
+}
+
+const struct export_operations ovl_export_operations = {
+ .encode_fh = ovl_encode_inode_fh,
+ .fh_to_dentry = ovl_fh_to_dentry,
+ .fh_to_parent = ovl_fh_to_parent,
+ .get_name = ovl_get_name,
+ .get_parent = ovl_get_parent,
+};
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 00b6b294272a..fcd97b783fa1 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -105,12 +105,20 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
* Lower hardlinks may be broken on copy up to different
* upper files, so we cannot use the lower origin st_ino
* for those different files, even for the same fs case.
+ *
+ * Similarly, several redirected dirs can point to the
+ * same dir on a lower layer. With the "verify_lower"
+ * feature, we do not use the lower origin st_ino, if
+ * we haven't verified that this redirect is unique.
+ *
* With inodes index enabled, it is safe to use st_ino
- * of an indexed hardlinked origin. The index validates
- * that the upper hardlink is not broken.
+ * of an indexed origin. The index validates that the
+ * upper hardlink is not broken and that a redirected
+ * dir is the only redirect to that origin.
*/
- if (is_dir || lowerstat.nlink == 1 ||
- ovl_test_flag(OVL_INDEX, d_inode(dentry)))
+ if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
+ (!ovl_verify_lower(dentry->d_sb) &&
+ (is_dir || lowerstat.nlink == 1)))
stat->ino = lowerstat.ino;
if (samefs)
@@ -343,8 +351,10 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
{
+ /* Copy up of disconnected dentry does not set upper alias */
if (ovl_dentry_upper(dentry) &&
- ovl_dentry_has_upper_alias(dentry))
+ (ovl_dentry_has_upper_alias(dentry) ||
+ (dentry->d_flags & DCACHE_DISCONNECTED)))
return false;
if (special_file(d_inode(dentry)->i_mode))
@@ -604,9 +614,25 @@ static int ovl_inode_set(struct inode *inode, void *data)
}
static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
- struct dentry *upperdentry)
+ struct dentry *upperdentry, bool strict)
{
/*
+ * For directories, @strict verify from lookup path performs consistency
+ * checks, so NULL lower/upper in dentry must match NULL lower/upper in
+ * inode. Non @strict verify from NFS handle decode path passes NULL for
+ * 'unknown' lower/upper.
+ */
+ if (S_ISDIR(inode->i_mode) && strict) {
+ /* Real lower dir moved to upper layer under us? */
+ if (!lowerdentry && ovl_inode_lower(inode))
+ return false;
+
+ /* Lookup of an uncovered redirect origin? */
+ if (!upperdentry && ovl_inode_upper(inode))
+ return false;
+ }
+
+ /*
* Allow non-NULL lower inode in ovl_inode even if lowerdentry is NULL.
* This happens when finding a copied up overlay inode for a renamed
* or hardlinked overlay dentry and lower dentry cannot be followed
@@ -625,14 +651,35 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
return true;
}
-struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
- struct dentry *index)
+struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
+ bool is_upper)
{
- struct dentry *lowerdentry = ovl_dentry_lower(dentry);
+ struct inode *inode, *key = d_inode(real);
+
+ inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key);
+ if (!inode)
+ return NULL;
+
+ if (!ovl_verify_inode(inode, is_upper ? NULL : real,
+ is_upper ? real : NULL, false)) {
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+
+ return inode;
+}
+
+struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
+ struct dentry *lowerdentry, struct dentry *index,
+ unsigned int numlower)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode;
/* Already indexed or could be indexed on copy up? */
- bool indexed = (index || (ovl_indexdir(dentry->d_sb) && !upperdentry));
+ bool indexed = (index || (ovl_indexdir(sb) && !upperdentry));
+ struct dentry *origin = indexed ? lowerdentry : NULL;
+ bool is_dir;
if (WARN_ON(upperdentry && indexed && !lowerdentry))
return ERR_PTR(-EIO);
@@ -641,17 +688,22 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
realinode = d_inode(lowerdentry);
/*
- * Copy up origin (lower) may exist for non-indexed upper, but we must
- * not use lower as hash key in that case.
- * Hash inodes that are or could be indexed by origin inode and
- * non-indexed upper inodes that could be hard linked by upper inode.
+ * Copy up origin (lower) may exist for non-indexed non-dir upper, but
+ * we must not use lower as hash key in that case.
+ * Hash non-dir that is or could be indexed by origin inode.
+ * Hash dir that is or could be merged by origin inode.
+ * Hash pure upper and non-indexed non-dir by upper inode.
+ * Hash non-indexed dir by upper inode for NFS export.
*/
- if (!S_ISDIR(realinode->i_mode) && (upperdentry || indexed)) {
- struct inode *key = d_inode(indexed ? lowerdentry :
- upperdentry);
- unsigned int nlink;
+ is_dir = S_ISDIR(realinode->i_mode);
+ if (is_dir && (indexed || !sb->s_export_op || !ofs->upper_mnt))
+ origin = lowerdentry;
+
+ if (upperdentry || origin) {
+ struct inode *key = d_inode(origin ?: upperdentry);
+ unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
- inode = iget5_locked(dentry->d_sb, (unsigned long) key,
+ inode = iget5_locked(sb, (unsigned long) key,
ovl_inode_test, ovl_inode_set, key);
if (!inode)
goto out_nomem;
@@ -660,7 +712,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
* Verify that the underlying files stored in the inode
* match those in the dentry.
*/
- if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) {
+ if (!ovl_verify_inode(inode, lowerdentry, upperdentry,
+ true)) {
iput(inode);
inode = ERR_PTR(-ESTALE);
goto out;
@@ -670,11 +723,12 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
goto out;
}
- nlink = ovl_get_nlink(lowerdentry, upperdentry,
- realinode->i_nlink);
+ /* Recalculate nlink for non-dir due to indexing */
+ if (!is_dir)
+ nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
set_nlink(inode, nlink);
} else {
- inode = new_inode(dentry->d_sb);
+ inode = new_inode(sb);
if (!inode)
goto out_nomem;
}
@@ -685,10 +739,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
ovl_set_flag(OVL_IMPURE, inode);
/* Check for non-merge dir that may have whiteouts */
- if (S_ISDIR(realinode->i_mode)) {
- struct ovl_entry *oe = dentry->d_fsdata;
-
- if (((upperdentry && lowerdentry) || oe->numlower > 1) ||
+ if (is_dir) {
+ if (((upperdentry && lowerdentry) || numlower > 1) ||
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
ovl_set_flag(OVL_WHITEOUTS, inode);
}
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index beb945e1963c..de3e6da1d5a5 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -9,6 +9,7 @@
#include <linux/fs.h>
#include <linux/cred.h>
+#include <linux/ctype.h>
#include <linux/namei.h>
#include <linux/xattr.h>
#include <linux/ratelimit.h>
@@ -84,15 +85,54 @@ invalid:
static int ovl_acceptable(void *ctx, struct dentry *dentry)
{
- return 1;
+ /*
+ * A non-dir origin may be disconnected, which is fine, because
+ * we only need it for its unique inode number.
+ */
+ if (!d_is_dir(dentry))
+ return 1;
+
+ /* Don't decode a deleted empty directory */
+ if (d_unhashed(dentry))
+ return 0;
+
+ /* Check if directory belongs to the layer we are decoding from */
+ return is_subdir(dentry, ((struct vfsmount *)ctx)->mnt_root);
}
-static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
+/*
+ * Check validity of an overlay file handle buffer.
+ *
+ * Return 0 for a valid file handle.
+ * Return -ENODATA for "origin unknown".
+ * Return <0 for an invalid file handle.
+ */
+int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
{
- int res;
+ if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len)
+ return -EINVAL;
+
+ if (fh->magic != OVL_FH_MAGIC)
+ return -EINVAL;
+
+ /* Treat larger version and unknown flags as "origin unknown" */
+ if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
+ return -ENODATA;
+
+ /* Treat endianness mismatch as "origin unknown" */
+ if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
+ (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
+ return -ENODATA;
+
+ return 0;
+}
+
+static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
+{
+ int res, err;
struct ovl_fh *fh = NULL;
- res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
+ res = vfs_getxattr(dentry, name, NULL, 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return NULL;
@@ -102,28 +142,20 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
if (res == 0)
return NULL;
- fh = kzalloc(res, GFP_KERNEL);
+ fh = kzalloc(res, GFP_KERNEL);
if (!fh)
return ERR_PTR(-ENOMEM);
- res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, fh, res);
+ res = vfs_getxattr(dentry, name, fh, res);
if (res < 0)
goto fail;
- if (res < sizeof(struct ovl_fh) || res < fh->len)
- goto invalid;
-
- if (fh->magic != OVL_FH_MAGIC)
+ err = ovl_check_fh_len(fh, res);
+ if (err < 0) {
+ if (err == -ENODATA)
+ goto out;
goto invalid;
-
- /* Treat larger version and unknown flags as "origin unknown" */
- if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
- goto out;
-
- /* Treat endianness mismatch as "origin unknown" */
- if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
- (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
- goto out;
+ }
return fh;
@@ -139,47 +171,41 @@ invalid:
goto out;
}
-static struct dentry *ovl_get_origin(struct dentry *dentry,
- struct vfsmount *mnt)
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
{
- struct dentry *origin = NULL;
- struct ovl_fh *fh = ovl_get_origin_fh(dentry);
+ struct dentry *real;
int bytes;
- if (IS_ERR_OR_NULL(fh))
- return (struct dentry *)fh;
-
/*
* Make sure that the stored uuid matches the uuid of the lower
* layer where file handle will be decoded.
*/
if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid))
- goto out;
+ return NULL;
bytes = (fh->len - offsetof(struct ovl_fh, fid));
- origin = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
- bytes >> 2, (int)fh->type,
- ovl_acceptable, NULL);
- if (IS_ERR(origin)) {
- /* Treat stale file handle as "origin unknown" */
- if (origin == ERR_PTR(-ESTALE))
- origin = NULL;
- goto out;
+ real = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
+ bytes >> 2, (int)fh->type,
+ ovl_acceptable, mnt);
+ if (IS_ERR(real)) {
+ /*
+ * Treat stale file handle to lower file as "origin unknown".
+ * upper file handle could become stale when upper file is
+ * unlinked and this information is needed to handle stale
+ * index entries correctly.
+ */
+ if (real == ERR_PTR(-ESTALE) &&
+ !(fh->flags & OVL_FH_FLAG_PATH_UPPER))
+ real = NULL;
+ return real;
}
- if (ovl_dentry_weird(origin) ||
- ((d_inode(origin)->i_mode ^ d_inode(dentry)->i_mode) & S_IFMT))
- goto invalid;
-
-out:
- kfree(fh);
- return origin;
+ if (ovl_dentry_weird(real)) {
+ dput(real);
+ return NULL;
+ }
-invalid:
- pr_warn_ratelimited("overlayfs: invalid origin (%pd2)\n", origin);
- dput(origin);
- origin = NULL;
- goto out;
+ return real;
}
static bool ovl_is_opaquedir(struct dentry *dentry)
@@ -284,47 +310,81 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
}
-static int ovl_check_origin(struct dentry *upperdentry,
- struct ovl_path *lower, unsigned int numlower,
- struct ovl_path **stackp, unsigned int *ctrp)
+int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
+ struct dentry *upperdentry, struct ovl_path **stackp)
{
- struct vfsmount *mnt;
struct dentry *origin = NULL;
int i;
- for (i = 0; i < numlower; i++) {
- mnt = lower[i].layer->mnt;
- origin = ovl_get_origin(upperdentry, mnt);
- if (IS_ERR(origin))
- return PTR_ERR(origin);
-
+ for (i = 0; i < ofs->numlower; i++) {
+ origin = ovl_decode_fh(fh, ofs->lower_layers[i].mnt);
if (origin)
break;
}
if (!origin)
- return 0;
+ return -ESTALE;
+ else if (IS_ERR(origin))
+ return PTR_ERR(origin);
+
+ if (upperdentry && !ovl_is_whiteout(upperdentry) &&
+ ((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
+ goto invalid;
- BUG_ON(*ctrp);
if (!*stackp)
*stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL);
if (!*stackp) {
dput(origin);
return -ENOMEM;
}
- **stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer};
- *ctrp = 1;
+ **stackp = (struct ovl_path){
+ .dentry = origin,
+ .layer = &ofs->lower_layers[i]
+ };
+
+ return 0;
+
+invalid:
+ pr_warn_ratelimited("overlayfs: invalid origin (%pd2, ftype=%x, origin ftype=%x).\n",
+ upperdentry, d_inode(upperdentry)->i_mode & S_IFMT,
+ d_inode(origin)->i_mode & S_IFMT);
+ dput(origin);
+ return -EIO;
+}
+
+static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
+ struct ovl_path **stackp, unsigned int *ctrp)
+{
+ struct ovl_fh *fh = ovl_get_fh(upperdentry, OVL_XATTR_ORIGIN);
+ int err;
+
+ if (IS_ERR_OR_NULL(fh))
+ return PTR_ERR(fh);
+
+ err = ovl_check_origin_fh(ofs, fh, upperdentry, stackp);
+ kfree(fh);
+
+ if (err) {
+ if (err == -ESTALE)
+ return 0;
+ return err;
+ }
+
+ if (WARN_ON(*ctrp))
+ return -EIO;
+ *ctrp = 1;
return 0;
}
/*
- * Verify that @fh matches the origin file handle stored in OVL_XATTR_ORIGIN.
+ * Verify that @fh matches the file handle stored in xattr @name.
* Return 0 on match, -ESTALE on mismatch, < 0 on error.
*/
-static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
+static int ovl_verify_fh(struct dentry *dentry, const char *name,
+ const struct ovl_fh *fh)
{
- struct ovl_fh *ofh = ovl_get_origin_fh(dentry);
+ struct ovl_fh *ofh = ovl_get_fh(dentry, name);
int err = 0;
if (!ofh)
@@ -341,28 +401,28 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
}
/*
- * Verify that an inode matches the origin file handle stored in upper inode.
+ * Verify that @real dentry matches the file handle stored in xattr @name.
*
- * If @set is true and there is no stored file handle, encode and store origin
- * file handle in OVL_XATTR_ORIGIN.
+ * If @set is true and there is no stored file handle, encode @real and store
+ * file handle in xattr @name.
*
- * Return 0 on match, -ESTALE on mismatch, < 0 on error.
+ * Return 0 on match, -ESTALE on mismatch, -ENODATA on no xattr, < 0 on error.
*/
-int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
- bool is_upper, bool set)
+int ovl_verify_set_fh(struct dentry *dentry, const char *name,
+ struct dentry *real, bool is_upper, bool set)
{
struct inode *inode;
struct ovl_fh *fh;
int err;
- fh = ovl_encode_fh(origin, is_upper);
+ fh = ovl_encode_fh(real, is_upper);
err = PTR_ERR(fh);
if (IS_ERR(fh))
goto fail;
- err = ovl_verify_origin_fh(dentry, fh);
+ err = ovl_verify_fh(dentry, name, fh);
if (set && err == -ENODATA)
- err = ovl_do_setxattr(dentry, OVL_XATTR_ORIGIN, fh, fh->len, 0);
+ err = ovl_do_setxattr(dentry, name, fh, fh->len, 0);
if (err)
goto fail;
@@ -371,45 +431,71 @@ out:
return err;
fail:
- inode = d_inode(origin);
- pr_warn_ratelimited("overlayfs: failed to verify origin (%pd2, ino=%lu, err=%i)\n",
- origin, inode ? inode->i_ino : 0, err);
+ inode = d_inode(real);
+ pr_warn_ratelimited("overlayfs: failed to verify %s (%pd2, ino=%lu, err=%i)\n",
+ is_upper ? "upper" : "origin", real,
+ inode ? inode->i_ino : 0, err);
goto out;
}
+/* Get upper dentry from index */
+struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
+{
+ struct ovl_fh *fh;
+ struct dentry *upper;
+
+ if (!d_is_dir(index))
+ return dget(index);
+
+ fh = ovl_get_fh(index, OVL_XATTR_UPPER);
+ if (IS_ERR_OR_NULL(fh))
+ return ERR_CAST(fh);
+
+ upper = ovl_decode_fh(fh, ofs->upper_mnt);
+ kfree(fh);
+
+ if (IS_ERR_OR_NULL(upper))
+ return upper ?: ERR_PTR(-ESTALE);
+
+ if (!d_is_dir(upper)) {
+ pr_warn_ratelimited("overlayfs: invalid index upper (%pd2, upper=%pd2).\n",
+ index, upper);
+ dput(upper);
+ return ERR_PTR(-EIO);
+ }
+
+ return upper;
+}
+
+/* Is this a leftover from create/whiteout of directory index entry? */
+static bool ovl_is_temp_index(struct dentry *index)
+{
+ return index->d_name.name[0] == '#';
+}
+
/*
* Verify that an index entry name matches the origin file handle stored in
* OVL_XATTR_ORIGIN and that origin file handle can be decoded to lower path.
* Return 0 on match, -ESTALE on mismatch or stale origin, < 0 on error.
*/
-int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
- unsigned int numlower)
+int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
{
struct ovl_fh *fh = NULL;
size_t len;
struct ovl_path origin = { };
struct ovl_path *stack = &origin;
- unsigned int ctr = 0;
+ struct dentry *upper = NULL;
int err;
if (!d_inode(index))
return 0;
- /*
- * Directory index entries are going to be used for looking up
- * redirected upper dirs by lower dir fh when decoding an overlay
- * file handle of a merge dir. Whiteout index entries are going to be
- * used as an indication that an exported overlay file handle should
- * be treated as stale (i.e. after unlink of the overlay inode).
- * We don't know the verification rules for directory and whiteout
- * index entries, because they have not been implemented yet, so return
- * EINVAL if those entries are found to abort the mount to avoid
- * corrupting an index that was created by a newer kernel.
- */
- err = -EINVAL;
- if (d_is_dir(index) || ovl_is_whiteout(index))
+ /* Cleanup leftover from index create/cleanup attempt */
+ err = -ESTALE;
+ if (ovl_is_temp_index(index))
goto fail;
+ err = -EINVAL;
if (index->d_name.len < sizeof(struct ovl_fh)*2)
goto fail;
@@ -420,26 +506,68 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
goto fail;
err = -EINVAL;
- if (hex2bin((u8 *)fh, index->d_name.name, len) || len != fh->len)
+ if (hex2bin((u8 *)fh, index->d_name.name, len))
goto fail;
- err = ovl_verify_origin_fh(index, fh);
+ err = ovl_check_fh_len(fh, len);
if (err)
goto fail;
- err = ovl_check_origin(index, lower, numlower, &stack, &ctr);
- if (!err && !ctr)
- err = -ESTALE;
+ /*
+ * Whiteout index entries are used as an indication that an exported
+ * overlay file handle should be treated as stale (i.e. after unlink
+ * of the overlay inode). These entries contain no origin xattr.
+ */
+ if (ovl_is_whiteout(index))
+ goto out;
+
+ /*
+ * Verifying directory index entries are not stale is expensive, so
+ * only verify stale dir index if NFS export is enabled.
+ */
+ if (d_is_dir(index) && !ofs->config.nfs_export)
+ goto out;
+
+ /*
+ * Directory index entries should have 'upper' xattr pointing to the
+ * real upper dir. Non-dir index entries are hardlinks to the upper
+ * real inode. For non-dir index, we can read the copy up origin xattr
+ * directly from the index dentry, but for dir index we first need to
+ * decode the upper directory.
+ */
+ upper = ovl_index_upper(ofs, index);
+ if (IS_ERR_OR_NULL(upper)) {
+ err = PTR_ERR(upper);
+ /*
+ * Directory index entries with no 'upper' xattr need to be
+ * removed. When dir index entry has a stale 'upper' xattr,
+ * we assume that upper dir was removed and we treat the dir
+ * index as orphan entry that needs to be whited out.
+ */
+ if (err == -ESTALE)
+ goto orphan;
+ else if (!err)
+ err = -ESTALE;
+ goto fail;
+ }
+
+ err = ovl_verify_fh(upper, OVL_XATTR_ORIGIN, fh);
+ dput(upper);
if (err)
goto fail;
- /* Check if index is orphan and don't warn before cleaning it */
- if (d_inode(index)->i_nlink == 1 &&
- ovl_get_nlink(origin.dentry, index, 0) == 0)
- err = -ENOENT;
+ /* Check if non-dir index is orphan and don't warn before cleaning it */
+ if (!d_is_dir(index) && d_inode(index)->i_nlink == 1) {
+ err = ovl_check_origin_fh(ofs, fh, index, &stack);
+ if (err)
+ goto fail;
+
+ if (ovl_get_nlink(origin.dentry, index, 0) == 0)
+ goto orphan;
+ }
- dput(origin.dentry);
out:
+ dput(origin.dentry);
kfree(fh);
return err;
@@ -447,6 +575,28 @@ fail:
pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, ftype=%x, err=%i)\n",
index, d_inode(index)->i_mode & S_IFMT, err);
goto out;
+
+orphan:
+ pr_warn_ratelimited("overlayfs: orphan index entry (%pd2, ftype=%x, nlink=%u)\n",
+ index, d_inode(index)->i_mode & S_IFMT,
+ d_inode(index)->i_nlink);
+ err = -ENOENT;
+ goto out;
+}
+
+static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name)
+{
+ char *n, *s;
+
+ n = kzalloc(fh->len * 2, GFP_KERNEL);
+ if (!n)
+ return -ENOMEM;
+
+ s = bin2hex(n, fh, fh->len);
+ *name = (struct qstr) QSTR_INIT(n, s - n);
+
+ return 0;
+
}
/*
@@ -466,35 +616,58 @@ fail:
*/
int ovl_get_index_name(struct dentry *origin, struct qstr *name)
{
- int err;
struct ovl_fh *fh;
- char *n, *s;
+ int err;
fh = ovl_encode_fh(origin, false);
if (IS_ERR(fh))
return PTR_ERR(fh);
- err = -ENOMEM;
- n = kzalloc(fh->len * 2, GFP_KERNEL);
- if (n) {
- s = bin2hex(n, fh, fh->len);
- *name = (struct qstr) QSTR_INIT(n, s - n);
- err = 0;
- }
- kfree(fh);
+ err = ovl_get_index_name_fh(fh, name);
+ kfree(fh);
return err;
+}
+
+/* Lookup index by file handle for NFS export */
+struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh)
+{
+ struct dentry *index;
+ struct qstr name;
+ int err;
+
+ err = ovl_get_index_name_fh(fh, &name);
+ if (err)
+ return ERR_PTR(err);
+
+ index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
+ kfree(name.name);
+ if (IS_ERR(index)) {
+ if (PTR_ERR(index) == -ENOENT)
+ index = NULL;
+ return index;
+ }
+ if (d_is_negative(index))
+ err = 0;
+ else if (ovl_is_whiteout(index))
+ err = -ESTALE;
+ else if (ovl_dentry_weird(index))
+ err = -EIO;
+ else
+ return index;
+
+ dput(index);
+ return ERR_PTR(err);
}
-static struct dentry *ovl_lookup_index(struct dentry *dentry,
- struct dentry *upper,
- struct dentry *origin)
+struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
+ struct dentry *origin, bool verify)
{
- struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
struct dentry *index;
struct inode *inode;
struct qstr name;
+ bool is_dir = d_is_dir(origin);
int err;
err = ovl_get_index_name(origin, &name);
@@ -518,8 +691,16 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
inode = d_inode(index);
if (d_is_negative(index)) {
goto out_dput;
- } else if (upper && d_inode(upper) != inode) {
- goto out_dput;
+ } else if (ovl_is_whiteout(index) && !verify) {
+ /*
+ * When index lookup is called with !verify for decoding an
+ * overlay file handle, a whiteout index implies that decode
+ * should treat file handle as stale and no need to print a
+ * warning about it.
+ */
+ dput(index);
+ index = ERR_PTR(-ESTALE);
+ goto out;
} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
/*
@@ -533,8 +714,25 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
index, d_inode(index)->i_mode & S_IFMT,
d_inode(origin)->i_mode & S_IFMT);
goto fail;
- }
+ } else if (is_dir && verify) {
+ if (!upper) {
+ pr_warn_ratelimited("overlayfs: suspected uncovered redirected dir found (origin=%pd2, index=%pd2).\n",
+ origin, index);
+ goto fail;
+ }
+ /* Verify that dir index 'upper' xattr points to upper dir */
+ err = ovl_verify_upper(index, upper, false);
+ if (err) {
+ if (err == -ESTALE) {
+ pr_warn_ratelimited("overlayfs: suspected multiply redirected dir found (upper=%pd2, origin=%pd2, index=%pd2).\n",
+ upper, origin, index);
+ }
+ goto fail;
+ }
+ } else if (upper && d_inode(upper) != inode) {
+ goto out_dput;
+ }
out:
kfree(name.name);
return index;
@@ -572,16 +770,25 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
return (idx < oe->numlower) ? idx + 1 : -1;
}
-static int ovl_find_layer(struct ovl_fs *ofs, struct ovl_path *path)
+/* Fix missing 'origin' xattr */
+static int ovl_fix_origin(struct dentry *dentry, struct dentry *lower,
+ struct dentry *upper)
{
- int i;
+ int err;
- for (i = 0; i < ofs->numlower; i++) {
- if (ofs->lower_layers[i].mnt == path->layer->mnt)
- break;
- }
+ if (ovl_check_origin_xattr(upper))
+ return 0;
+
+ err = ovl_want_write(dentry);
+ if (err)
+ return err;
+
+ err = ovl_set_origin(dentry, lower, upper);
+ if (!err)
+ err = ovl_set_impure(dentry->d_parent, upper->d_parent);
- return i;
+ ovl_drop_write(dentry);
+ return err;
}
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
@@ -594,6 +801,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
struct ovl_path *stack = NULL;
struct dentry *upperdir, *upperdentry = NULL;
+ struct dentry *origin = NULL;
struct dentry *index = NULL;
unsigned int ctr = 0;
struct inode *inode = NULL;
@@ -638,8 +846,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
* number - it's the same as if we held a reference
* to a dentry in lower layer that was moved under us.
*/
- err = ovl_check_origin(upperdentry, roe->lowerstack,
- roe->numlower, &stack, &ctr);
+ err = ovl_check_origin(ofs, upperdentry, &stack, &ctr);
if (err)
goto out_put_upper;
}
@@ -674,6 +881,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (!this)
continue;
+ /*
+ * If no origin fh is stored in upper of a merge dir, store fh
+ * of lower dir and set upper parent "impure".
+ */
+ if (upperdentry && !ctr && !ofs->noxattr) {
+ err = ovl_fix_origin(dentry, this, upperdentry);
+ if (err) {
+ dput(this);
+ goto out_put;
+ }
+ }
+
+ /*
+ * When "verify_lower" feature is enabled, do not merge with a
+ * lower dir that does not match a stored origin xattr. In any
+ * case, only verified origin is used for index lookup.
+ */
+ if (upperdentry && !ctr && ovl_verify_lower(dentry->d_sb)) {
+ err = ovl_verify_origin(upperdentry, this, false);
+ if (err) {
+ dput(this);
+ break;
+ }
+
+ /* Bless lower dir as verified origin */
+ origin = this;
+ }
+
stack[ctr].dentry = this;
stack[ctr].layer = lower.layer;
ctr++;
@@ -693,25 +928,30 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
*/
err = -EPERM;
if (d.redirect && !ofs->config.redirect_follow) {
- pr_warn_ratelimited("overlay: refusing to follow redirect for (%pd2)\n", dentry);
+ pr_warn_ratelimited("overlayfs: refusing to follow redirect for (%pd2)\n",
+ dentry);
goto out_put;
}
if (d.redirect && d.redirect[0] == '/' && poe != roe) {
poe = roe;
-
/* Find the current layer on the root dentry */
- i = ovl_find_layer(ofs, &lower);
- if (WARN_ON(i == ofs->numlower))
- break;
+ i = lower.layer->idx - 1;
}
}
- /* Lookup index by lower inode and verify it matches upper inode */
- if (ctr && !d.is_dir && ovl_indexdir(dentry->d_sb)) {
- struct dentry *origin = stack[0].dentry;
+ /*
+ * Lookup index by lower inode and verify it matches upper inode.
+ * We only trust dir index if we verified that lower dir matches
+ * origin, otherwise dir index entries may be inconsistent and we
+ * ignore them. Always lookup index of non-dir and non-upper.
+ */
+ if (ctr && (!upperdentry || !d.is_dir))
+ origin = stack[0].dentry;
- index = ovl_lookup_index(dentry, upperdentry, origin);
+ if (origin && ovl_indexdir(dentry->d_sb) &&
+ (!d.is_dir || ovl_index_all(dentry->d_sb))) {
+ index = ovl_lookup_index(ofs, upperdentry, origin, true);
if (IS_ERR(index)) {
err = PTR_ERR(index);
index = NULL;
@@ -724,17 +964,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (!oe)
goto out_put;
- oe->opaque = upperopaque;
memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr);
dentry->d_fsdata = oe;
+ if (upperopaque)
+ ovl_dentry_set_opaque(dentry);
+
if (upperdentry)
ovl_dentry_set_upper_alias(dentry);
else if (index)
upperdentry = dget(index);
if (upperdentry || ctr) {
- inode = ovl_get_inode(dentry, upperdentry, index);
+ if (ctr)
+ origin = stack[0].dentry;
+ inode = ovl_get_inode(dentry->d_sb, upperdentry, origin, index,
+ ctr);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_free_oe;
@@ -748,9 +993,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
dput(index);
kfree(stack);
kfree(d.redirect);
- d_add(dentry, inode);
-
- return NULL;
+ return d_splice_alias(inode, dentry);
out_free_oe:
dentry->d_fsdata = NULL;
@@ -771,9 +1014,9 @@ out:
bool ovl_lower_positive(struct dentry *dentry)
{
- struct ovl_entry *oe = dentry->d_fsdata;
struct ovl_entry *poe = dentry->d_parent->d_fsdata;
const struct qstr *name = &dentry->d_name;
+ const struct cred *old_cred;
unsigned int i;
bool positive = false;
bool done = false;
@@ -783,12 +1026,13 @@ bool ovl_lower_positive(struct dentry *dentry)
* whiteout.
*/
if (!dentry->d_inode)
- return oe->opaque;
+ return ovl_dentry_is_opaque(dentry);
/* Negative upper -> positive lower */
if (!ovl_dentry_upper(dentry))
return true;
+ old_cred = ovl_override_creds(dentry->d_sb);
/* Positive upper -> have to look up lower to see whether it exists */
for (i = 0; !done && !positive && i < poe->numlower; i++) {
struct dentry *this;
@@ -818,6 +1062,7 @@ bool ovl_lower_positive(struct dentry *dentry)
dput(this);
}
}
+ revert_creds(old_cred);
return positive;
}
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index b489099ccd49..0df25a9c94bd 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -27,8 +27,9 @@ enum ovl_path_type {
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
+#define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
-enum ovl_flag {
+enum ovl_inode_flag {
/* Pure upper dir that may contain non pure upper entries */
OVL_IMPURE,
/* Non-merge dir that may contain whiteout entries */
@@ -36,6 +37,11 @@ enum ovl_flag {
OVL_INDEX,
};
+enum ovl_entry_flag {
+ OVL_E_UPPER_ALIAS,
+ OVL_E_OPAQUE,
+};
+
/*
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
* where:
@@ -62,6 +68,9 @@ enum ovl_flag {
#error Endianness not defined
#endif
+/* The type returned by overlay exportfs ops when encoding an ovl_fh handle */
+#define OVL_FILEID 0xfb
+
/* On-disk and in-memeory format for redirect by file handle */
struct ovl_fh {
u8 version; /* 0 */
@@ -194,6 +203,8 @@ const struct cred *ovl_override_creds(struct super_block *sb);
struct super_block *ovl_same_sb(struct super_block *sb);
bool ovl_can_decode_fh(struct super_block *sb);
struct dentry *ovl_indexdir(struct super_block *sb);
+bool ovl_index_all(struct super_block *sb);
+bool ovl_verify_lower(struct super_block *sb);
struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
bool ovl_dentry_remote(struct dentry *dentry);
bool ovl_dentry_weird(struct dentry *dentry);
@@ -210,6 +221,9 @@ struct inode *ovl_inode_lower(struct inode *inode);
struct inode *ovl_inode_real(struct inode *inode);
struct ovl_dir_cache *ovl_dir_cache(struct inode *inode);
void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache);
+void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry);
+void ovl_dentry_clear_flag(unsigned long flag, struct dentry *dentry);
+bool ovl_dentry_test_flag(unsigned long flag, struct dentry *dentry);
bool ovl_dentry_is_opaque(struct dentry *dentry);
bool ovl_dentry_is_whiteout(struct dentry *dentry);
void ovl_dentry_set_opaque(struct dentry *dentry);
@@ -238,6 +252,7 @@ void ovl_clear_flag(unsigned long flag, struct inode *inode);
bool ovl_test_flag(unsigned long flag, struct inode *inode);
bool ovl_inuse_trylock(struct dentry *dentry);
void ovl_inuse_unlock(struct dentry *dentry);
+bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry, bool *locked);
void ovl_nlink_end(struct dentry *dentry, bool locked);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
@@ -249,15 +264,35 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
/* namei.c */
-int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
- bool is_upper, bool set);
-int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
- unsigned int numlower);
+int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt);
+int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
+ struct dentry *upperdentry, struct ovl_path **stackp);
+int ovl_verify_set_fh(struct dentry *dentry, const char *name,
+ struct dentry *real, bool is_upper, bool set);
+struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index);
+int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);
int ovl_get_index_name(struct dentry *origin, struct qstr *name);
+struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
+struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
+ struct dentry *origin, bool verify);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
-struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
+struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags);
bool ovl_lower_positive(struct dentry *dentry);
+static inline int ovl_verify_origin(struct dentry *upper,
+ struct dentry *origin, bool set)
+{
+ return ovl_verify_set_fh(upper, OVL_XATTR_ORIGIN, origin, false, set);
+}
+
+static inline int ovl_verify_upper(struct dentry *index,
+ struct dentry *upper, bool set)
+{
+ return ovl_verify_set_fh(index, OVL_XATTR_UPPER, upper, true, set);
+}
+
/* readdir.c */
extern const struct file_operations ovl_dir_operations;
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
@@ -267,8 +302,7 @@ void ovl_dir_cache_free(struct inode *inode);
int ovl_check_d_type_supported(struct path *realpath);
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level);
-int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
- struct ovl_path *lower, unsigned int numlower);
+int ovl_indexdir_cleanup(struct ovl_fs *ofs);
/* inode.c */
int ovl_set_nlink_upper(struct dentry *dentry);
@@ -291,8 +325,11 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name);
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
-struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
- struct dentry *index);
+struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
+ bool is_upper);
+struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
+ struct dentry *lowerdentry, struct dentry *index,
+ unsigned int numlower);
static inline void ovl_copyattr(struct inode *from, struct inode *to)
{
to->i_uid = from->i_uid;
@@ -306,6 +343,8 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
/* dir.c */
extern const struct inode_operations ovl_dir_inode_operations;
struct dentry *ovl_lookup_temp(struct dentry *workdir);
+int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
+ struct dentry *dentry);
struct cattr {
dev_t rdev;
umode_t mode;
@@ -321,4 +360,9 @@ int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_flags(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
-struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper);
+struct ovl_fh *ovl_encode_fh(struct dentry *real, bool is_upper);
+int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
+ struct dentry *upper);
+
+/* export.c */
+extern const struct export_operations ovl_export_operations;
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index 9d0bc03bf6e4..bfef6edcc111 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -17,11 +17,14 @@ struct ovl_config {
bool redirect_follow;
const char *redirect_mode;
bool index;
+ bool nfs_export;
};
struct ovl_layer {
struct vfsmount *mnt;
dev_t pseudo_dev;
+ /* Index of this layer in fs root (upper == 0) */
+ int idx;
};
struct ovl_path {
@@ -58,8 +61,7 @@ struct ovl_fs {
struct ovl_entry {
union {
struct {
- unsigned long has_upper;
- bool opaque;
+ unsigned long flags;
};
struct rcu_head rcu;
};
@@ -69,6 +71,11 @@ struct ovl_entry {
struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
+static inline struct ovl_entry *OVL_E(struct dentry *dentry)
+{
+ return (struct ovl_entry *) dentry->d_fsdata;
+}
+
struct ovl_inode {
struct ovl_dir_cache *cache;
const char *redirect;
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 8c98578d27a1..c11f5c0906c3 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -593,8 +593,15 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
return ERR_PTR(res);
}
if (list_empty(&cache->entries)) {
- /* Good oportunity to get rid of an unnecessary "impure" flag */
- ovl_do_removexattr(ovl_dentry_upper(dentry), OVL_XATTR_IMPURE);
+ /*
+ * A good opportunity to get rid of an unneeded "impure" flag.
+ * Removing the "impure" xattr is best effort.
+ */
+ if (!ovl_want_write(dentry)) {
+ ovl_do_removexattr(ovl_dentry_upper(dentry),
+ OVL_XATTR_IMPURE);
+ ovl_drop_write(dentry);
+ }
ovl_clear_flag(OVL_IMPURE, d_inode(dentry));
kfree(cache);
return NULL;
@@ -769,10 +776,14 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
struct dentry *dentry = file->f_path.dentry;
struct file *realfile = od->realfile;
+ /* Nothing to sync for lower */
+ if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
+ return 0;
+
/*
* Need to check if we started out being a lower dir, but got copied up
*/
- if (!od->is_upper && OVL_TYPE_UPPER(ovl_path_type(dentry))) {
+ if (!od->is_upper) {
struct inode *inode = file_inode(file);
realfile = READ_ONCE(od->upperfile);
@@ -858,8 +869,11 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
int err;
struct ovl_cache_entry *p, *n;
struct rb_root root = RB_ROOT;
+ const struct cred *old_cred;
+ old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_dir_read_merged(dentry, list, &root);
+ revert_creds(old_cred);
if (err)
return err;
@@ -1016,13 +1030,13 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
}
}
-int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
- struct ovl_path *lower, unsigned int numlower)
+int ovl_indexdir_cleanup(struct ovl_fs *ofs)
{
int err;
+ struct dentry *indexdir = ofs->indexdir;
struct dentry *index = NULL;
- struct inode *dir = dentry->d_inode;
- struct path path = { .mnt = mnt, .dentry = dentry };
+ struct inode *dir = indexdir->d_inode;
+ struct path path = { .mnt = ofs->upper_mnt, .dentry = indexdir };
LIST_HEAD(list);
struct rb_root root = RB_ROOT;
struct ovl_cache_entry *p;
@@ -1046,19 +1060,40 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
if (p->len == 2 && p->name[1] == '.')
continue;
}
- index = lookup_one_len(p->name, dentry, p->len);
+ index = lookup_one_len(p->name, indexdir, p->len);
if (IS_ERR(index)) {
err = PTR_ERR(index);
index = NULL;
break;
}
- err = ovl_verify_index(index, lower, numlower);
- /* Cleanup stale and orphan index entries */
- if (err && (err == -ESTALE || err == -ENOENT))
+ err = ovl_verify_index(ofs, index);
+ if (!err) {
+ goto next;
+ } else if (err == -ESTALE) {
+ /* Cleanup stale index entries */
+ err = ovl_cleanup(dir, index);
+ } else if (err != -ENOENT) {
+ /*
+ * Abort mount to avoid corrupting the index if
+ * an incompatible index entry was found or on out
+ * of memory.
+ */
+ break;
+ } else if (ofs->config.nfs_export) {
+ /*
+ * Whiteout orphan index to block future open by
+ * handle after overlay nlink dropped to zero.
+ */
+ err = ovl_cleanup_and_whiteout(indexdir, dir, index);
+ } else {
+ /* Cleanup orphan index entries */
err = ovl_cleanup(dir, index);
+ }
+
if (err)
break;
+next:
dput(index);
index = NULL;
}
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 76440feb79f6..9ee37c76091d 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -45,6 +45,11 @@ module_param_named(index, ovl_index_def, bool, 0644);
MODULE_PARM_DESC(ovl_index_def,
"Default to on or off for the inodes index feature");
+static bool ovl_nfs_export_def = IS_ENABLED(CONFIG_OVERLAY_FS_NFS_EXPORT);
+module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644);
+MODULE_PARM_DESC(ovl_nfs_export_def,
+ "Default to on or off for the NFS export feature");
+
static void ovl_entry_stack_free(struct ovl_entry *oe)
{
unsigned int i;
@@ -211,6 +216,7 @@ static void ovl_destroy_inode(struct inode *inode)
struct ovl_inode *oi = OVL_I(inode);
dput(oi->__upperdentry);
+ iput(oi->lower);
kfree(oi->redirect);
ovl_dir_cache_free(inode);
mutex_destroy(&oi->lock);
@@ -341,6 +347,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
if (ofs->config.index != ovl_index_def)
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
+ if (ofs->config.nfs_export != ovl_nfs_export_def)
+ seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
+ "on" : "off");
return 0;
}
@@ -373,6 +382,8 @@ enum {
OPT_REDIRECT_DIR,
OPT_INDEX_ON,
OPT_INDEX_OFF,
+ OPT_NFS_EXPORT_ON,
+ OPT_NFS_EXPORT_OFF,
OPT_ERR,
};
@@ -384,6 +395,8 @@ static const match_table_t ovl_tokens = {
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
{OPT_INDEX_ON, "index=on"},
{OPT_INDEX_OFF, "index=off"},
+ {OPT_NFS_EXPORT_ON, "nfs_export=on"},
+ {OPT_NFS_EXPORT_OFF, "nfs_export=off"},
{OPT_ERR, NULL}
};
@@ -490,6 +503,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->index = false;
break;
+ case OPT_NFS_EXPORT_ON:
+ config->nfs_export = true;
+ break;
+
+ case OPT_NFS_EXPORT_OFF:
+ config->nfs_export = false;
+ break;
+
default:
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
return -EINVAL;
@@ -520,10 +541,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
bool retried = false;
bool locked = false;
- err = mnt_want_write(mnt);
- if (err)
- goto out_err;
-
inode_lock_nested(dir, I_MUTEX_PARENT);
locked = true;
@@ -588,7 +605,6 @@ retry:
goto out_err;
}
out_unlock:
- mnt_drop_write(mnt);
if (locked)
inode_unlock(dir);
@@ -700,12 +716,16 @@ static int ovl_lower_dir(const char *name, struct path *path,
*remote = true;
/*
- * The inodes index feature needs to encode and decode file
- * handles, so it requires that all layers support them.
+ * The inodes index feature and NFS export need to encode and decode
+ * file handles, so they require that all layers support them.
*/
- if (ofs->config.index && !ovl_can_decode_fh(path->dentry->d_sb)) {
+ if ((ofs->config.nfs_export ||
+ (ofs->config.index && ofs->config.upperdir)) &&
+ !ovl_can_decode_fh(path->dentry->d_sb)) {
ofs->config.index = false;
- pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n", name);
+ ofs->config.nfs_export = false;
+ pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
+ name);
}
return 0;
@@ -929,12 +949,17 @@ out:
static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
{
+ struct vfsmount *mnt = ofs->upper_mnt;
struct dentry *temp;
int err;
+ err = mnt_want_write(mnt);
+ if (err)
+ return err;
+
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
if (!ofs->workdir)
- return 0;
+ goto out;
/*
* Upper should support d_type, else whiteouts are visible. Given
@@ -944,7 +969,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
*/
err = ovl_check_d_type_supported(workpath);
if (err < 0)
- return err;
+ goto out;
/*
* We allowed this configuration and don't want to break users over
@@ -967,7 +992,9 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
if (err) {
ofs->noxattr = true;
- pr_warn("overlayfs: upper fs does not support xattr.\n");
+ ofs->config.index = false;
+ pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off.\n");
+ err = 0;
} else {
vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
}
@@ -979,7 +1006,15 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
}
- return 0;
+ /* NFS export of r/w mount depends on index */
+ if (ofs->config.nfs_export && !ofs->config.index) {
+ pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
+ }
+
+out:
+ mnt_drop_write(mnt);
+ return err;
}
static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
@@ -1026,11 +1061,16 @@ out:
static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
struct path *upperpath)
{
+ struct vfsmount *mnt = ofs->upper_mnt;
int err;
+ err = mnt_want_write(mnt);
+ if (err)
+ return err;
+
/* Verify lower root is upper root origin */
err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
- false, true);
+ true);
if (err) {
pr_err("overlayfs: failed to verify upper root origin\n");
goto out;
@@ -1038,23 +1078,33 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) {
- /* Verify upper root is index dir origin */
- err = ovl_verify_origin(ofs->indexdir, upperpath->dentry,
- true, true);
+ /*
+ * Verify upper root is exclusively associated with index dir.
+ * Older kernels stored upper fh in "trusted.overlay.origin"
+ * xattr. If that xattr exists, verify that it is a match to
+ * upper dir file handle. In any case, verify or set xattr
+ * "trusted.overlay.upper" to indicate that index may have
+ * directory entries.
+ */
+ if (ovl_check_origin_xattr(ofs->indexdir)) {
+ err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN,
+ upperpath->dentry, true, false);
+ if (err)
+ pr_err("overlayfs: failed to verify index dir 'origin' xattr\n");
+ }
+ err = ovl_verify_upper(ofs->indexdir, upperpath->dentry, true);
if (err)
- pr_err("overlayfs: failed to verify index dir origin\n");
+ pr_err("overlayfs: failed to verify index dir 'upper' xattr\n");
/* Cleanup bad/stale/orphan index entries */
if (!err)
- err = ovl_indexdir_cleanup(ofs->indexdir,
- ofs->upper_mnt,
- oe->lowerstack,
- oe->numlower);
+ err = ovl_indexdir_cleanup(ofs);
}
if (err || !ofs->indexdir)
pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
out:
+ mnt_drop_write(mnt);
return err;
}
@@ -1094,6 +1144,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
ofs->lower_layers[ofs->numlower].mnt = mnt;
ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
+ ofs->lower_layers[ofs->numlower].idx = i + 1;
ofs->numlower++;
/* Check if all lower layers are on same sb */
@@ -1131,6 +1182,10 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
} else if (!ofs->config.upperdir && stacklen == 1) {
pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n");
goto out_err;
+ } else if (!ofs->config.upperdir && ofs->config.nfs_export &&
+ ofs->config.redirect_follow) {
+ pr_warn("overlayfs: NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
}
err = -ENOMEM;
@@ -1207,6 +1262,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
goto out_err;
ofs->config.index = ovl_index_def;
+ ofs->config.nfs_export = ovl_nfs_export_def;
err = ovl_parse_opt((char *) data, &ofs->config);
if (err)
goto out_err;
@@ -1257,13 +1313,26 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (err)
goto out_free_oe;
- if (!ofs->indexdir)
+ /* Force r/o mount with no index dir */
+ if (!ofs->indexdir) {
+ dput(ofs->workdir);
+ ofs->workdir = NULL;
sb->s_flags |= SB_RDONLY;
+ }
+
}
- /* Show index=off/on in /proc/mounts for any of the reasons above */
- if (!ofs->indexdir)
+ /* Show index=off in /proc/mounts for forced r/o mount */
+ if (!ofs->indexdir) {
ofs->config.index = false;
+ if (ofs->upper_mnt && ofs->config.nfs_export) {
+ pr_warn("overlayfs: NFS export requires an index dir, falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
+ }
+ }
+
+ if (ofs->config.nfs_export)
+ sb->s_export_op = &ovl_export_operations;
/* Never override disk quota limits or use reserved space */
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
@@ -1279,15 +1348,15 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (!root_dentry)
goto out_free_oe;
+ root_dentry->d_fsdata = oe;
+
mntput(upperpath.mnt);
if (upperpath.dentry) {
- oe->has_upper = true;
+ ovl_dentry_set_upper_alias(root_dentry);
if (ovl_is_impuredir(upperpath.dentry))
ovl_set_flag(OVL_IMPURE, d_inode(root_dentry));
}
- root_dentry->d_fsdata = oe;
-
/* Root is always merge -> can have whiteouts */
ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index d6bb1c9f5e7a..930784a26623 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -63,6 +63,22 @@ struct dentry *ovl_indexdir(struct super_block *sb)
return ofs->indexdir;
}
+/* Index all files on copy up. For now only enabled for NFS export */
+bool ovl_index_all(struct super_block *sb)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+
+ return ofs->config.nfs_export && ofs->config.index;
+}
+
+/* Verify lower origin on lookup. For now only enabled for NFS export */
+bool ovl_verify_lower(struct super_block *sb)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+
+ return ofs->config.nfs_export && ofs->config.index;
+}
+
struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
{
size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
@@ -194,10 +210,24 @@ void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache)
OVL_I(inode)->cache = cache;
}
+void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry)
+{
+ set_bit(flag, &OVL_E(dentry)->flags);
+}
+
+void ovl_dentry_clear_flag(unsigned long flag, struct dentry *dentry)
+{
+ clear_bit(flag, &OVL_E(dentry)->flags);
+}
+
+bool ovl_dentry_test_flag(unsigned long flag, struct dentry *dentry)
+{
+ return test_bit(flag, &OVL_E(dentry)->flags);
+}
+
bool ovl_dentry_is_opaque(struct dentry *dentry)
{
- struct ovl_entry *oe = dentry->d_fsdata;
- return oe->opaque;
+ return ovl_dentry_test_flag(OVL_E_OPAQUE, dentry);
}
bool ovl_dentry_is_whiteout(struct dentry *dentry)
@@ -207,28 +237,23 @@ bool ovl_dentry_is_whiteout(struct dentry *dentry)
void ovl_dentry_set_opaque(struct dentry *dentry)
{
- struct ovl_entry *oe = dentry->d_fsdata;
-
- oe->opaque = true;
+ ovl_dentry_set_flag(OVL_E_OPAQUE, dentry);
}
/*
- * For hard links it's possible for ovl_dentry_upper() to return positive, while
- * there's no actual upper alias for the inode. Copy up code needs to know
- * about the existence of the upper alias, so it can't use ovl_dentry_upper().
+ * For hard links and decoded file handles, it's possible for ovl_dentry_upper()
+ * to return positive, while there's no actual upper alias for the inode.
+ * Copy up code needs to know about the existence of the upper alias, so it
+ * can't use ovl_dentry_upper().
*/
bool ovl_dentry_has_upper_alias(struct dentry *dentry)
{
- struct ovl_entry *oe = dentry->d_fsdata;
-
- return oe->has_upper;
+ return ovl_dentry_test_flag(OVL_E_UPPER_ALIAS, dentry);
}
void ovl_dentry_set_upper_alias(struct dentry *dentry)
{
- struct ovl_entry *oe = dentry->d_fsdata;
-
- oe->has_upper = true;
+ ovl_dentry_set_flag(OVL_E_UPPER_ALIAS, dentry);
}
bool ovl_redirect_dir(struct super_block *sb)
@@ -257,7 +282,7 @@ void ovl_inode_init(struct inode *inode, struct dentry *upperdentry,
if (upperdentry)
OVL_I(inode)->__upperdentry = upperdentry;
if (lowerdentry)
- OVL_I(inode)->lower = d_inode(lowerdentry);
+ OVL_I(inode)->lower = igrab(d_inode(lowerdentry));
ovl_copyattr(d_inode(upperdentry ?: lowerdentry), inode);
}
@@ -273,7 +298,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
*/
smp_wmb();
OVL_I(inode)->__upperdentry = upperdentry;
- if (!S_ISDIR(upperinode->i_mode) && inode_unhashed(inode)) {
+ if (inode_unhashed(inode)) {
inode->i_private = upperinode;
__insert_inode_hash(inode, (unsigned long) upperinode);
}
@@ -447,10 +472,32 @@ void ovl_inuse_unlock(struct dentry *dentry)
}
}
+/*
+ * Does this overlay dentry need to be indexed on copy up?
+ */
+bool ovl_need_index(struct dentry *dentry)
+{
+ struct dentry *lower = ovl_dentry_lower(dentry);
+
+ if (!lower || !ovl_indexdir(dentry->d_sb))
+ return false;
+
+ /* Index all files for NFS export and consistency verification */
+ if (ovl_index_all(dentry->d_sb))
+ return true;
+
+ /* Index only lower hardlinks on copy up */
+ if (!d_is_dir(lower) && d_inode(lower)->i_nlink > 1)
+ return true;
+
+ return false;
+}
+
/* Caller must hold OVL_I(inode)->lock */
static void ovl_cleanup_index(struct dentry *dentry)
{
- struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode;
+ struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
+ struct inode *dir = indexdir->d_inode;
struct dentry *lowerdentry = ovl_dentry_lower(dentry);
struct dentry *upperdentry = ovl_dentry_upper(dentry);
struct dentry *index = NULL;
@@ -463,7 +510,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
goto fail;
inode = d_inode(upperdentry);
- if (inode->i_nlink != 1) {
+ if (!S_ISDIR(inode->i_mode) && inode->i_nlink != 1) {
pr_warn_ratelimited("overlayfs: cleanup linked index (%pd2, ino=%lu, nlink=%u)\n",
upperdentry, inode->i_ino, inode->i_nlink);
/*
@@ -481,13 +528,17 @@ static void ovl_cleanup_index(struct dentry *dentry)
}
inode_lock_nested(dir, I_MUTEX_PARENT);
- /* TODO: whiteout instead of cleanup to block future open by handle */
- index = lookup_one_len(name.name, ovl_indexdir(dentry->d_sb), name.len);
+ index = lookup_one_len(name.name, indexdir, name.len);
err = PTR_ERR(index);
- if (!IS_ERR(index))
- err = ovl_cleanup(dir, index);
- else
+ if (IS_ERR(index)) {
index = NULL;
+ } else if (ovl_index_all(dentry->d_sb)) {
+ /* Whiteout orphan index to block future open by handle */
+ err = ovl_cleanup_and_whiteout(indexdir, dir, index);
+ } else {
+ /* Cleanup orphan index entries */
+ err = ovl_cleanup(dir, index);
+ }
inode_unlock(dir);
if (err)
@@ -512,16 +563,16 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
const struct cred *old_cred;
int err;
- if (!d_inode(dentry) || d_is_dir(dentry))
+ if (!d_inode(dentry))
return 0;
/*
* With inodes index is enabled, we store the union overlay nlink
- * in an xattr on the index inode. When whiting out lower hardlinks
+ * in an xattr on the index inode. When whiting out an indexed lower,
* we need to decrement the overlay persistent nlink, but before the
* first copy up, we have no upper index inode to store the xattr.
*
- * As a workaround, before whiteout/rename over of a lower hardlink,
+ * As a workaround, before whiteout/rename over an indexed lower,
* copy up to create the upper index. Creating the upper index will
* initialize the overlay nlink, so it could be dropped if unlink
* or rename succeeds.
@@ -529,8 +580,7 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
* TODO: implement metadata only index copy up when called with
* ovl_copy_up_flags(dentry, O_PATH).
*/
- if (ovl_indexdir(dentry->d_sb) && !ovl_dentry_has_upper_alias(dentry) &&
- d_inode(ovl_dentry_lower(dentry))->i_nlink > 1) {
+ if (ovl_need_index(dentry) && !ovl_dentry_has_upper_alias(dentry)) {
err = ovl_copy_up(dentry);
if (err)
return err;
@@ -540,7 +590,7 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
if (err)
return err;
- if (!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
+ if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
goto out;
old_cred = ovl_override_creds(dentry->d_sb);
diff --git a/fs/pipe.c b/fs/pipe.c
index a449ca0ec0c6..0913aed7fd0d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -35,11 +35,6 @@
*/
unsigned int pipe_max_size = 1048576;
-/*
- * Minimum pipe size, as required by POSIX
- */
-unsigned int pipe_min_size = PAGE_SIZE;
-
/* Maximum allocatable pages per user. Hard limit is unset by default, soft
* matches default values.
*/
@@ -610,12 +605,21 @@ static unsigned long account_pipe_buffers(struct user_struct *user,
static bool too_many_pipe_buffers_soft(unsigned long user_bufs)
{
- return pipe_user_pages_soft && user_bufs >= pipe_user_pages_soft;
+ unsigned long soft_limit = READ_ONCE(pipe_user_pages_soft);
+
+ return soft_limit && user_bufs > soft_limit;
}
static bool too_many_pipe_buffers_hard(unsigned long user_bufs)
{
- return pipe_user_pages_hard && user_bufs >= pipe_user_pages_hard;
+ unsigned long hard_limit = READ_ONCE(pipe_user_pages_hard);
+
+ return hard_limit && user_bufs > hard_limit;
+}
+
+static bool is_unprivileged_user(void)
+{
+ return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
}
struct pipe_inode_info *alloc_pipe_info(void)
@@ -624,22 +628,23 @@ struct pipe_inode_info *alloc_pipe_info(void)
unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
struct user_struct *user = get_current_user();
unsigned long user_bufs;
+ unsigned int max_size = READ_ONCE(pipe_max_size);
pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_ACCOUNT);
if (pipe == NULL)
goto out_free_uid;
- if (pipe_bufs * PAGE_SIZE > pipe_max_size && !capable(CAP_SYS_RESOURCE))
- pipe_bufs = pipe_max_size >> PAGE_SHIFT;
+ if (pipe_bufs * PAGE_SIZE > max_size && !capable(CAP_SYS_RESOURCE))
+ pipe_bufs = max_size >> PAGE_SHIFT;
user_bufs = account_pipe_buffers(user, 0, pipe_bufs);
- if (too_many_pipe_buffers_soft(user_bufs)) {
+ if (too_many_pipe_buffers_soft(user_bufs) && is_unprivileged_user()) {
user_bufs = account_pipe_buffers(user, pipe_bufs, 1);
pipe_bufs = 1;
}
- if (too_many_pipe_buffers_hard(user_bufs))
+ if (too_many_pipe_buffers_hard(user_bufs) && is_unprivileged_user())
goto out_revert_acct;
pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
@@ -1020,18 +1025,16 @@ const struct file_operations pipefifo_fops = {
* Currently we rely on the pipe array holding a power-of-2 number
* of pages. Returns 0 on error.
*/
-unsigned int round_pipe_size(unsigned int size)
+unsigned int round_pipe_size(unsigned long size)
{
- unsigned long nr_pages;
-
- if (size < pipe_min_size)
- size = pipe_min_size;
-
- nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- if (nr_pages == 0)
+ if (size > (1U << 31))
return 0;
- return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
+ /* Minimum pipe size, as required by POSIX */
+ if (size < PAGE_SIZE)
+ return PAGE_SIZE;
+
+ return roundup_pow_of_two(size);
}
/*
@@ -1046,8 +1049,6 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
long ret = 0;
size = round_pipe_size(arg);
- if (size == 0)
- return -EINVAL;
nr_pages = size >> PAGE_SHIFT;
if (!nr_pages)
@@ -1069,7 +1070,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
if (nr_pages > pipe->buffers &&
(too_many_pipe_buffers_hard(user_bufs) ||
too_many_pipe_buffers_soft(user_bufs)) &&
- !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
+ is_unprivileged_user()) {
ret = -EPERM;
goto out_revert_acct;
}
@@ -1125,16 +1126,6 @@ out_revert_acct:
}
/*
- * This should work even if CONFIG_PROC_FS isn't set, as proc_dopipe_max_size
- * will return an error.
- */
-int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf,
- size_t *lenp, loff_t *ppos)
-{
- return proc_dopipe_max_size(table, write, buf, lenp, ppos);
-}
-
-/*
* After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same
* location, so checking ->i_pipe is not enough to verify that this is a
* pipe.
diff --git a/fs/proc/array.c b/fs/proc/array.c
index d67a72dcb92c..598803576e4c 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -736,16 +736,10 @@ static int children_seq_open(struct inode *inode, struct file *file)
return ret;
}
-int children_seq_release(struct inode *inode, struct file *file)
-{
- seq_release(inode, file);
- return 0;
-}
-
const struct file_operations proc_tid_children_operations = {
.open = children_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = children_seq_release,
+ .release = seq_release,
};
#endif /* CONFIG_PROC_CHILDREN */
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 60316b52d659..9298324325ed 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -75,6 +75,7 @@
#include <linux/ptrace.h>
#include <linux/tracehook.h>
#include <linux/printk.h>
+#include <linux/cache.h>
#include <linux/cgroup.h>
#include <linux/cpuset.h>
#include <linux/audit.h>
@@ -100,6 +101,8 @@
#include "internal.h"
#include "fd.h"
+#include "../../lib/kstrtox.h"
+
/* NOTE:
* Implementing inode permission operations in /proc is almost
* certainly an error. Permission checks need to happen during
@@ -110,8 +113,8 @@
* in /proc for a task before it execs a suid executable.
*/
-static u8 nlink_tid;
-static u8 nlink_tgid;
+static u8 nlink_tid __ro_after_init;
+static u8 nlink_tgid __ro_after_init;
struct pid_entry {
const char *name;
@@ -1370,7 +1373,7 @@ static ssize_t proc_fail_nth_write(struct file *file, const char __user *buf,
task = get_proc_task(file_inode(file));
if (!task)
return -ESRCH;
- WRITE_ONCE(task->fail_nth, n);
+ task->fail_nth = n;
put_task_struct(task);
return count;
@@ -1386,8 +1389,7 @@ static ssize_t proc_fail_nth_read(struct file *file, char __user *buf,
task = get_proc_task(file_inode(file));
if (!task)
return -ESRCH;
- len = snprintf(numbuf, sizeof(numbuf), "%u\n",
- READ_ONCE(task->fail_nth));
+ len = snprintf(numbuf, sizeof(numbuf), "%u\n", task->fail_nth);
len = simple_read_from_buffer(buf, count, ppos, numbuf, len);
put_task_struct(task);
@@ -1907,8 +1909,33 @@ end_instantiate:
static int dname_to_vma_addr(struct dentry *dentry,
unsigned long *start, unsigned long *end)
{
- if (sscanf(dentry->d_name.name, "%lx-%lx", start, end) != 2)
+ const char *str = dentry->d_name.name;
+ unsigned long long sval, eval;
+ unsigned int len;
+
+ len = _parse_integer(str, 16, &sval);
+ if (len & KSTRTOX_OVERFLOW)
+ return -EINVAL;
+ if (sval != (unsigned long)sval)
return -EINVAL;
+ str += len;
+
+ if (*str != '-')
+ return -EINVAL;
+ str++;
+
+ len = _parse_integer(str, 16, &eval);
+ if (len & KSTRTOX_OVERFLOW)
+ return -EINVAL;
+ if (eval != (unsigned long)eval)
+ return -EINVAL;
+ str += len;
+
+ if (*str != '\0')
+ return -EINVAL;
+
+ *start = sval;
+ *end = eval;
return 0;
}
@@ -2000,9 +2027,9 @@ out:
}
struct map_files_info {
+ unsigned long start;
+ unsigned long end;
fmode_t mode;
- unsigned int len;
- unsigned char name[4*sizeof(long)+2]; /* max: %lx-%lx\0 */
};
/*
@@ -2172,10 +2199,9 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
if (++pos <= ctx->pos)
continue;
+ info.start = vma->vm_start;
+ info.end = vma->vm_end;
info.mode = vma->vm_file->f_mode;
- info.len = snprintf(info.name,
- sizeof(info.name), "%lx-%lx",
- vma->vm_start, vma->vm_end);
if (flex_array_put(fa, i++, &info, GFP_KERNEL))
BUG();
}
@@ -2183,9 +2209,13 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
up_read(&mm->mmap_sem);
for (i = 0; i < nr_files; i++) {
+ char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
+ unsigned int len;
+
p = flex_array_get(fa, i);
+ len = snprintf(buf, sizeof(buf), "%lx-%lx", p->start, p->end);
if (!proc_fill_cache(file, ctx,
- p->name, p->len,
+ buf, len,
proc_map_files_instantiate,
task,
(void *)(unsigned long)p->mode))
@@ -3018,11 +3048,11 @@ static const struct inode_operations proc_tgid_base_inode_operations = {
static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid)
{
struct dentry *dentry, *leader, *dir;
- char buf[PROC_NUMBUF];
+ char buf[10 + 1];
struct qstr name;
name.name = buf;
- name.len = snprintf(buf, sizeof(buf), "%d", pid);
+ name.len = snprintf(buf, sizeof(buf), "%u", pid);
/* no ->d_hash() rejects on procfs */
dentry = d_hash_and_lookup(mnt->mnt_root, &name);
if (dentry) {
@@ -3034,7 +3064,7 @@ static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid)
return;
name.name = buf;
- name.len = snprintf(buf, sizeof(buf), "%d", tgid);
+ name.len = snprintf(buf, sizeof(buf), "%u", tgid);
leader = d_hash_and_lookup(mnt->mnt_root, &name);
if (!leader)
goto out;
@@ -3046,7 +3076,7 @@ static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid)
goto out_put_leader;
name.name = buf;
- name.len = snprintf(buf, sizeof(buf), "%d", pid);
+ name.len = snprintf(buf, sizeof(buf), "%u", pid);
dentry = d_hash_and_lookup(dir, &name);
if (dentry) {
d_invalidate(dentry);
@@ -3225,14 +3255,14 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)
for (iter = next_tgid(ns, iter);
iter.task;
iter.tgid += 1, iter = next_tgid(ns, iter)) {
- char name[PROC_NUMBUF];
+ char name[10 + 1];
int len;
cond_resched();
if (!has_pid_permissions(ns, iter.task, HIDEPID_INVISIBLE))
continue;
- len = snprintf(name, sizeof(name), "%d", iter.tgid);
+ len = snprintf(name, sizeof(name), "%u", iter.tgid);
ctx->pos = iter.tgid + TGID_OFFSET;
if (!proc_fill_cache(file, ctx, name, len,
proc_pid_instantiate, iter.task, NULL)) {
@@ -3560,10 +3590,10 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx)
for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns);
task;
task = next_tid(task), ctx->pos++) {
- char name[PROC_NUMBUF];
+ char name[10 + 1];
int len;
tid = task_pid_nr_ns(task, ns);
- len = snprintf(name, sizeof(name), "%d", tid);
+ len = snprintf(name, sizeof(name), "%u", tid);
if (!proc_fill_cache(file, ctx, name, len,
proc_task_instantiate, task, NULL)) {
/* returning this tgid failed, save it as the first
diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c
index 290ba85cb900..a8ac48aebd59 100644
--- a/fs/proc/consoles.c
+++ b/fs/proc/consoles.c
@@ -55,8 +55,7 @@ static int show_console_dev(struct seq_file *m, void *v)
if (dev)
seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev));
- seq_printf(m, "\n");
-
+ seq_putc(m, '\n');
return 0;
}
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 96fc70225e54..6b80cd1e419a 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -236,7 +236,7 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx,
for (fd = ctx->pos - 2;
fd < files_fdtable(files)->max_fds;
fd++, ctx->pos++) {
- char name[PROC_NUMBUF];
+ char name[10 + 1];
int len;
if (!fcheck_files(files, fd))
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 793a67574668..5d709fa8f3a2 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -28,7 +28,7 @@
static DEFINE_RWLOCK(proc_subdir_lock);
-static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)
+static int proc_match(const char *name, struct proc_dir_entry *de, unsigned int len)
{
if (len < de->namelen)
return -1;
@@ -60,7 +60,7 @@ static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
struct proc_dir_entry *de = rb_entry(node,
struct proc_dir_entry,
subdir_node);
- int result = proc_match(len, name, de);
+ int result = proc_match(name, de, len);
if (result < 0)
node = node->rb_left;
@@ -84,7 +84,7 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir,
struct proc_dir_entry *this = rb_entry(*new,
struct proc_dir_entry,
subdir_node);
- int result = proc_match(de->namelen, de->name, this);
+ int result = proc_match(de->name, this, de->namelen);
parent = *new;
if (result < 0)
@@ -211,8 +211,8 @@ void proc_free_inum(unsigned int inum)
* Don't create negative dentries here, return -ENOENT by hand
* instead.
*/
-struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
- struct dentry *dentry)
+struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
+ struct proc_dir_entry *de)
{
struct inode *inode;
@@ -235,7 +235,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
- return proc_lookup_de(PDE(dir), dir, dentry);
+ return proc_lookup_de(dir, dentry, PDE(dir));
}
/*
@@ -247,8 +247,8 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
* value of the readdir() call, as long as it's non-negative
* for success..
*/
-int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
- struct dir_context *ctx)
+int proc_readdir_de(struct file *file, struct dir_context *ctx,
+ struct proc_dir_entry *de)
{
int i;
@@ -292,7 +292,7 @@ int proc_readdir(struct file *file, struct dir_context *ctx)
{
struct inode *inode = file_inode(file);
- return proc_readdir_de(PDE(inode), file, ctx);
+ return proc_readdir_de(file, ctx, PDE(inode));
}
/*
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 8dacaabb9f37..6e8724958116 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -5,6 +5,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/cache.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
@@ -52,7 +53,7 @@ static void proc_evict_inode(struct inode *inode)
}
}
-static struct kmem_cache * proc_inode_cachep;
+static struct kmem_cache *proc_inode_cachep __ro_after_init;
static struct inode *proc_alloc_inode(struct super_block *sb)
{
@@ -128,12 +129,12 @@ enum {BIAS = -1U<<31};
static inline int use_pde(struct proc_dir_entry *pde)
{
- return atomic_inc_unless_negative(&pde->in_use);
+ return likely(atomic_inc_unless_negative(&pde->in_use));
}
static void unuse_pde(struct proc_dir_entry *pde)
{
- if (atomic_dec_return(&pde->in_use) == BIAS)
+ if (unlikely(atomic_dec_return(&pde->in_use) == BIAS))
complete(pde->pde_unload_completion);
}
@@ -166,7 +167,7 @@ static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
spin_lock(&pde->pde_unload_lock);
/* After ->release. */
list_del(&pdeo->lh);
- if (pdeo->c)
+ if (unlikely(pdeo->c))
complete(pdeo->c);
kfree(pdeo);
}
@@ -420,7 +421,7 @@ static const char *proc_get_link(struct dentry *dentry,
struct delayed_call *done)
{
struct proc_dir_entry *pde = PDE(inode);
- if (unlikely(!use_pde(pde)))
+ if (!use_pde(pde))
return ERR_PTR(-EINVAL);
set_delayed_call(done, proc_put_link, pde);
return pde->data;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 4a67188c8d74..d697c8ab0a14 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -31,24 +31,28 @@ struct mempolicy;
* subdir_node is used to build the rb tree "subdir" of the parent.
*/
struct proc_dir_entry {
+ /*
+ * number of callers into module in progress;
+ * negative -> it's going away RSN
+ */
+ atomic_t in_use;
+ atomic_t count; /* use count */
+ struct list_head pde_openers; /* who did ->open, but not ->release */
+ /* protects ->pde_openers and all struct pde_opener instances */
+ spinlock_t pde_unload_lock;
+ struct completion *pde_unload_completion;
+ const struct inode_operations *proc_iops;
+ const struct file_operations *proc_fops;
+ void *data;
unsigned int low_ino;
- umode_t mode;
nlink_t nlink;
kuid_t uid;
kgid_t gid;
loff_t size;
- const struct inode_operations *proc_iops;
- const struct file_operations *proc_fops;
struct proc_dir_entry *parent;
struct rb_root_cached subdir;
struct rb_node subdir_node;
- void *data;
- atomic_t count; /* use count */
- atomic_t in_use; /* number of callers into module in progress; */
- /* negative -> it's going away RSN */
- struct completion *pde_unload_completion;
- struct list_head pde_openers; /* who did ->open, but not ->release */
- spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
+ umode_t mode;
u8 namelen;
char name[];
} __randomize_layout;
@@ -149,10 +153,9 @@ extern bool proc_fill_cache(struct file *, struct dir_context *, const char *, i
* generic.c
*/
extern struct dentry *proc_lookup(struct inode *, struct dentry *, unsigned int);
-extern struct dentry *proc_lookup_de(struct proc_dir_entry *, struct inode *,
- struct dentry *);
+struct dentry *proc_lookup_de(struct inode *, struct dentry *, struct proc_dir_entry *);
extern int proc_readdir(struct file *, struct dir_context *);
-extern int proc_readdir_de(struct proc_dir_entry *, struct file *, struct dir_context *);
+int proc_readdir_de(struct file *, struct dir_context *, struct proc_dir_entry *);
static inline struct proc_dir_entry *pde_get(struct proc_dir_entry *pde)
{
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 4bc85cb8be6a..e8a93bc8285d 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -512,23 +512,15 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
return -EFAULT;
} else {
if (kern_addr_valid(start)) {
- unsigned long n;
-
/*
* Using bounce buffer to bypass the
* hardened user copy kernel text checks.
*/
- memcpy(buf, (char *) start, tsz);
- n = copy_to_user(buffer, buf, tsz);
- /*
- * We cannot distinguish between fault on source
- * and fault on destination. When this happens
- * we clear too and hope it will trigger the
- * EFAULT again.
- */
- if (n) {
- if (clear_user(buffer + tsz - n,
- n))
+ if (probe_kernel_read(buf, (void *) start, tsz)) {
+ if (clear_user(buffer, tsz))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(buffer, buf, tsz))
return -EFAULT;
}
} else {
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index a2bf369c923d..68c06ae7888c 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -135,7 +135,7 @@ static struct dentry *proc_tgid_net_lookup(struct inode *dir,
de = ERR_PTR(-ENOENT);
net = get_proc_task_net(dir);
if (net != NULL) {
- de = proc_lookup_de(net->proc_net, dir, dentry);
+ de = proc_lookup_de(dir, dentry, net->proc_net);
put_net(net);
}
return de;
@@ -172,7 +172,7 @@ static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
ret = -EINVAL;
net = get_proc_task_net(file_inode(file));
if (net != NULL) {
- ret = proc_readdir_de(net->proc_net, file, ctx);
+ ret = proc_readdir_de(file, ctx, net->proc_net);
put_net(net);
}
return ret;
diff --git a/fs/proc/self.c b/fs/proc/self.c
index 31326bb23b8b..4d7d061696b3 100644
--- a/fs/proc/self.c
+++ b/fs/proc/self.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/cache.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/pid_namespace.h>
@@ -17,11 +18,11 @@ static const char *proc_self_get_link(struct dentry *dentry,
if (!tgid)
return ERR_PTR(-ENOENT);
- /* 11 for max length of signed int in decimal + NULL term */
- name = kmalloc(12, dentry ? GFP_KERNEL : GFP_ATOMIC);
+ /* max length of unsigned int in decimal + NULL term */
+ name = kmalloc(10 + 1, dentry ? GFP_KERNEL : GFP_ATOMIC);
if (unlikely(!name))
return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD);
- sprintf(name, "%d", tgid);
+ sprintf(name, "%u", tgid);
set_delayed_call(done, kfree_link, name);
return name;
}
@@ -30,7 +31,7 @@ static const struct inode_operations proc_self_inode_operations = {
.get_link = proc_self_get_link,
};
-static unsigned self_inum;
+static unsigned self_inum __ro_after_init;
int proc_setup_self(struct super_block *s)
{
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c
index b813e3b529f2..9d2efaca499f 100644
--- a/fs/proc/thread_self.c
+++ b/fs/proc/thread_self.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/cache.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/pid_namespace.h>
@@ -18,11 +19,10 @@ static const char *proc_thread_self_get_link(struct dentry *dentry,
if (!pid)
return ERR_PTR(-ENOENT);
- name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF,
- dentry ? GFP_KERNEL : GFP_ATOMIC);
+ name = kmalloc(10 + 6 + 10 + 1, dentry ? GFP_KERNEL : GFP_ATOMIC);
if (unlikely(!name))
return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD);
- sprintf(name, "%d/task/%d", tgid, pid);
+ sprintf(name, "%u/task/%u", tgid, pid);
set_delayed_call(done, kfree_link, name);
return name;
}
@@ -31,7 +31,7 @@ static const struct inode_operations proc_thread_self_inode_operations = {
.get_link = proc_thread_self_get_link,
};
-static unsigned thread_self_inum;
+static unsigned thread_self_inum __ro_after_init;
int proc_setup_thread_self(struct super_block *s)
{
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 885d445afa0d..a45f0af22a60 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -1178,18 +1178,16 @@ fs_initcall(vmcore_init);
/* Cleanup function for vmcore module. */
void vmcore_cleanup(void)
{
- struct list_head *pos, *next;
-
if (proc_vmcore) {
proc_remove(proc_vmcore);
proc_vmcore = NULL;
}
/* clear the vmcore list. */
- list_for_each_safe(pos, next, &vmcore_list) {
+ while (!list_empty(&vmcore_list)) {
struct vmcore *m;
- m = list_entry(pos, struct vmcore, list);
+ m = list_first_entry(&vmcore_list, struct vmcore, list);
list_del(&m->list);
kfree(m);
}
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 691032107f8c..c3129b131e4d 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -41,7 +41,6 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
-#include <linux/hardirq.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 2b67bda2021b..58eba92a0e41 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/sysfs/dir.c - sysfs core and dir operation implementation
*
@@ -5,12 +6,10 @@
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
*
- * This file is released under the GPLv2.
- *
* Please see Documentation/filesystems/sysfs.txt for more information.
*/
-#undef DEBUG
+#define pr_fmt(fmt) "sysfs: " fmt
#include <linux/fs.h>
#include <linux/kobject.h>
@@ -27,8 +26,8 @@ void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
if (buf)
kernfs_path(parent, buf, PATH_MAX);
- WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
- buf, name);
+ pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
+ dump_stack();
kfree(buf);
}
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 39c75a86c67f..5c13f29bfcdb 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/sysfs/file.c - sysfs regular (text) file implementation
*
@@ -5,14 +6,11 @@
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
*
- * This file is released under the GPLv2.
- *
* Please see Documentation/filesystems/sysfs.txt for more information.
*/
#include <linux/module.h>
#include <linux/kobject.h>
-#include <linux/kallsyms.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -70,8 +68,8 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
* indicate truncated result or overflow in normal use cases.
*/
if (count >= (ssize_t)PAGE_SIZE) {
- print_symbol("fill_read_buffer: %s returned bad count\n",
- (unsigned long)ops->show);
+ printk("fill_read_buffer: %pS returned bad count\n",
+ ops->show);
/* Try to struggle along */
count = PAGE_SIZE - 1;
}
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index ac2de0ed69ad..4802ec0e1e3a 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/sysfs/group.c - Operations for adding/removing multiple files at once.
*
@@ -5,9 +6,6 @@
* Copyright (c) 2003 Open Source Development Lab
* Copyright (c) 2013 Greg Kroah-Hartman
* Copyright (c) 2013 The Linux Foundation
- *
- * This file is released undert the GPL v2.
- *
*/
#include <linux/kobject.h>
@@ -406,6 +404,6 @@ int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
kernfs_put(entry);
kernfs_put(target);
- return IS_ERR(link) ? PTR_ERR(link) : 0;
+ return PTR_ERR_OR_ZERO(link);
}
EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index fb49510c5dcf..b428d317ae92 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/sysfs/symlink.c - operations for initializing and mounting sysfs
*
@@ -5,13 +6,9 @@
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
*
- * This file is released under the GPLv2.
- *
* Please see Documentation/filesystems/sysfs.txt for more information.
*/
-#define DEBUG
-
#include <linux/fs.h>
#include <linux/magic.h>
#include <linux/mount.h>
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index aecb15f84557..8664db25a9a6 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fs/sysfs/symlink.c - sysfs symlink implementation
*
@@ -5,8 +6,6 @@
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
*
- * This file is released under the GPLv2.
- *
* Please see Documentation/filesystems/sysfs.txt for more information.
*/
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 0e2f1cccb812..d098e015fcc9 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -1,11 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* fs/sysfs/sysfs.h - sysfs internal header file
*
* Copyright (c) 2001-3 Patrick Mochel
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
- *
- * This file is released under the GPLv2.
*/
#ifndef __SYSFS_INTERNAL_H
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index a2ea4856e67b..9d7fb88e172e 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -1138,38 +1138,24 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
struct ubifs_info *c = dir->i_sb->s_fs_info;
int err, len = strlen(symname);
int sz_change = CALC_DENT_SIZE(len);
- struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
- struct fscrypt_symlink_data *sd = NULL;
+ struct fscrypt_str disk_link;
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
.new_ino_d = ALIGN(len, 8),
.dirtied_ino = 1 };
struct fscrypt_name nm;
- if (ubifs_crypt_is_encrypted(dir)) {
- err = fscrypt_get_encryption_info(dir);
- if (err)
- goto out_budg;
-
- if (!fscrypt_has_encryption_key(dir)) {
- err = -EPERM;
- goto out_budg;
- }
+ dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry,
+ symname, dir->i_ino);
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
- sizeof(struct fscrypt_symlink_data));
- }
+ err = fscrypt_prepare_symlink(dir, symname, len, UBIFS_MAX_INO_DATA,
+ &disk_link);
+ if (err)
+ return err;
/*
* Budget request settings: new inode, new direntry and changing parent
* directory inode.
*/
-
- dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry,
- symname, dir->i_ino);
-
- if (disk_link.len > UBIFS_MAX_INO_DATA)
- return -ENAMETOOLONG;
-
err = ubifs_budget_space(c, &req);
if (err)
return err;
@@ -1191,38 +1177,20 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
goto out_inode;
}
- if (ubifs_crypt_is_encrypted(dir)) {
- struct qstr istr = QSTR_INIT(symname, len);
- struct fscrypt_str ostr;
-
- sd = kzalloc(disk_link.len, GFP_NOFS);
- if (!sd) {
- err = -ENOMEM;
- goto out_inode;
- }
-
- ostr.name = sd->encrypted_path;
- ostr.len = disk_link.len;
-
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
- if (err) {
- kfree(sd);
+ if (IS_ENCRYPTED(inode)) {
+ disk_link.name = ui->data; /* encrypt directly into ui->data */
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
+ if (err)
goto out_inode;
- }
-
- sd->len = cpu_to_le16(ostr.len);
- disk_link.name = (char *)sd;
} else {
+ memcpy(ui->data, disk_link.name, disk_link.len);
inode->i_link = ui->data;
}
- memcpy(ui->data, disk_link.name, disk_link.len);
- ((char *)ui->data)[disk_link.len - 1] = '\0';
-
/*
* The terminating zero byte is not written to the flash media and it
* is put just to make later in-memory string processing simpler. Thus,
- * data length is @len, not @len + %1.
+ * data length is @disk_link.len - 1, not @disk_link.len.
*/
ui->data_len = disk_link.len - 1;
inode->i_size = ubifs_inode(inode)->ui_size = disk_link.len - 1;
@@ -1240,11 +1208,10 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
goto out_cancel;
mutex_unlock(&dir_ui->ui_mutex);
- ubifs_release_budget(c, &req);
insert_inode_hash(inode);
d_instantiate(dentry, inode);
- fscrypt_free_filename(&nm);
- return 0;
+ err = 0;
+ goto out_fname;
out_cancel:
dir->i_size -= sz_change;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 9fe194a4fa9b..cf348ba99238 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1629,49 +1629,17 @@ static const char *ubifs_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
- int err;
- struct fscrypt_symlink_data *sd;
struct ubifs_inode *ui = ubifs_inode(inode);
- struct fscrypt_str cstr;
- struct fscrypt_str pstr;
- if (!ubifs_crypt_is_encrypted(inode))
+ if (!IS_ENCRYPTED(inode))
return ui->data;
if (!dentry)
return ERR_PTR(-ECHILD);
- err = fscrypt_get_encryption_info(inode);
- if (err)
- return ERR_PTR(err);
-
- sd = (struct fscrypt_symlink_data *)ui->data;
- cstr.name = sd->encrypted_path;
- cstr.len = le16_to_cpu(sd->len);
-
- if (cstr.len == 0)
- return ERR_PTR(-ENOENT);
-
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > ui->data_len)
- return ERR_PTR(-EIO);
-
- err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
- if (err)
- return ERR_PTR(err);
-
- err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
- if (err) {
- fscrypt_fname_free_buffer(&pstr);
- return ERR_PTR(err);
- }
-
- pstr.name[pstr.len] = '\0';
-
- set_delayed_call(done, kfree_link, pstr.name);
- return pstr.name;
+ return fscrypt_get_symlink(inode, ui->data, ui->data_len, done);
}
-
const struct address_space_operations ubifs_file_address_operations = {
.readpage = ubifs_readpage,
.writepage = ubifs_writepage,
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 0beb285b143d..b16ef162344a 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -379,9 +379,7 @@ out:
}
done:
clear_inode(inode);
-#ifdef CONFIG_UBIFS_FS_ENCRYPTION
- fscrypt_put_encryption_info(inode, NULL);
-#endif
+ fscrypt_put_encryption_info(inode);
}
static void ubifs_dirty_inode(struct inode *inode, int flags)
diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c
index 14626b34d13e..0927a4b2ecaf 100644
--- a/fs/udf/udftime.c
+++ b/fs/udf/udftime.c
@@ -62,6 +62,11 @@ udf_disk_stamp_to_time(struct timespec *dest, struct timestamp src)
dest->tv_sec -= offset * 60;
dest->tv_nsec = 1000 * (src.centiseconds * 10000 +
src.hundredsOfMicroseconds * 100 + src.microseconds);
+ /*
+ * Sanitize nanosecond field since reportedly some filesystems are
+ * recorded with bogus sub-second values.
+ */
+ dest->tv_nsec %= NSEC_PER_SEC;
return dest;
}
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index b6ba80e05bff..8254b8b3690f 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1467,11 +1467,14 @@ static void init_once(void *foo)
static int __init init_inodecache(void)
{
- ufs_inode_cachep = kmem_cache_create("ufs_inode_cache",
- sizeof(struct ufs_inode_info),
- 0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_ACCOUNT),
- init_once);
+ ufs_inode_cachep = kmem_cache_create_usercopy("ufs_inode_cache",
+ sizeof(struct ufs_inode_info), 0,
+ (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT),
+ offsetof(struct ufs_inode_info, i_u1.i_symlink),
+ sizeof_field(struct ufs_inode_info,
+ i_u1.i_symlink),
+ init_once);
if (ufs_inode_cachep == NULL)
return -ENOMEM;
return 0;
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 46af6aa60a8e..a55f7a45fa78 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -118,8 +118,8 @@ xfs_mount_validate_sb(
bool check_inprogress,
bool check_version)
{
- u32 agcount = 0;
- u32 rem;
+ uint32_t agcount = 0;
+ uint32_t rem;
if (sbp->sb_magicnum != XFS_SB_MAGIC) {
xfs_warn(mp, "bad magic number");
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 8601275cc5e6..9ea08326f876 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1048,7 +1048,7 @@ __xfs_filemap_fault(
if (IS_DAX(inode)) {
pfn_t pfn;
- ret = dax_iomap_fault(vmf, pe_size, &pfn, &xfs_iomap_ops);
+ ret = dax_iomap_fault(vmf, pe_size, &pfn, NULL, &xfs_iomap_ops);
if (ret & VM_FAULT_NEEDDSYNC)
ret = dax_finish_sync_fault(vmf, pe_size, pfn);
} else {
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index f3e0001f9992..7aba628dc527 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1666,9 +1666,12 @@ xfs_fs_fill_super(
"DAX unsupported by block device. Turning off DAX.");
mp->m_flags &= ~XFS_MOUNT_DAX;
}
- if (xfs_sb_version_hasreflink(&mp->m_sb))
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
xfs_alert(mp,
"DAX and reflink cannot be used together!");
+ error = -EINVAL;
+ goto out_filestream_unmount;
+ }
}
if (mp->m_flags & XFS_MOUNT_DISCARD) {
@@ -1681,15 +1684,18 @@ xfs_fs_fill_super(
}
}
- if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
- if (mp->m_sb.sb_rblocks) {
- xfs_alert(mp,
- "EXPERIMENTAL reverse mapping btree not compatible with realtime device!");
- error = -EINVAL;
- goto out_filestream_unmount;
- }
+ if (xfs_sb_version_hasreflink(&mp->m_sb) && mp->m_sb.sb_rblocks) {
+ xfs_alert(mp,
+ "reflink not compatible with realtime device!");
+ error = -EINVAL;
+ goto out_filestream_unmount;
+ }
+
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb) && mp->m_sb.sb_rblocks) {
xfs_alert(mp,
- "EXPERIMENTAL reverse mapping btree feature enabled. Use at your own risk!");
+ "reverse mapping btree not compatible with realtime device!");
+ error = -EINVAL;
+ goto out_filestream_unmount;
}
error = xfs_mountfs(mp);
diff --git a/fs/xfs/xfs_super.h b/fs/xfs/xfs_super.h
index fcc5dfc70aa0..8cee8e8050e3 100644
--- a/fs/xfs/xfs_super.h
+++ b/fs/xfs/xfs_super.h
@@ -44,6 +44,12 @@ extern void xfs_qm_exit(void);
# define XFS_REALTIME_STRING
#endif
+#ifdef CONFIG_XFS_ONLINE_SCRUB
+# define XFS_SCRUB_STRING "scrub, "
+#else
+# define XFS_SCRUB_STRING
+#endif
+
#ifdef DEBUG
# define XFS_DBG_STRING "debug"
#else
@@ -54,6 +60,7 @@ extern void xfs_qm_exit(void);
#define XFS_BUILD_OPTIONS XFS_ACL_STRING \
XFS_SECURITY_STRING \
XFS_REALTIME_STRING \
+ XFS_SCRUB_STRING \
XFS_DBG_STRING /* DBG must be last */
struct xfs_inode;