Re: [PATCH 23/32] VFS: Implement logging through fs_context [ver #8]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2018-05-25 at 01:07 +0100, David Howells wrote:
> Implement the ability for filesystems to log error, warning and
> informational messages through the fs_context.  These can be extracted by
> userspace by reading from an fd created by fsopen().
> 
> Error messages are prefixed with "e ", warnings with "w " and informational
> messages with "i ".
[]
> diff --git a/fs/fs_context.c b/fs/fs_context.c
[]
> +/**
> + * logfc - Log a message to a filesystem context
> + * @fc: The filesystem context to log to.
> + * @fmt: The format of the buffer.
> + */
> +void logfc(struct fs_context *fc, const char *fmt, ...)
> +{
> +	static const char store_failure[] = "OOM: Can't store error string";
> +	struct fc_log *log = fc->log;
> +	unsigned int logsize = ARRAY_SIZE(log->buffer);
> +	const char *p;
> +	va_list va;
> +	char *q;
> +	u8 freeable, index;
> +
> +	if (!log)
> +		return;
> +
> +	va_start(va, fmt);
> +	if (!strchr(fmt, '%')) {
> +		p = fmt;
> +		goto unformatted_string;
> +	}
> +	if (strcmp(fmt, "%s") == 0) {
> +		p = va_arg(va, const char *);
> +		goto unformatted_string;
> +	}
> +
> +	q = kvasprintf(GFP_KERNEL, fmt, va);
> +copied_string:
> +	if (!q)
> +		goto store_failure;
> +	freeable = 1;
> +	goto store_string;
> +
> +unformatted_string:
> +	if ((unsigned long)p >= (unsigned long)__start_rodata &&
> +	    (unsigned long)p <  (unsigned long)__end_rodata)
> +		goto const_string;
> +	if (within_module_core((unsigned long)p, log->owner))
> +		goto const_string;
> +	q = kstrdup(p, GFP_KERNEL);
> +	goto copied_string;
> +
> +store_failure:
> +	p = store_failure;
> +const_string:
> +	q = (char *)p;
> +	freeable = 0;
> +store_string:
> +	index = log->head & (logsize - 1);
> +	if ((int)log->head - (int)log->tail == 8) {
> +		/* The buffer is full, discard the oldest message */
> +		if (log->need_free & (1 << index))
> +			kfree(log->buffer[index]);
> +		log->tail++;
> +	}
> +
> +	log->buffer[index] = q;
> +	log->need_free &= ~(1 << index);
> +	log->need_free |= freeable << index;
> +	log->head++;
> +	va_end(va);
> +}
> +EXPORT_SYMBOL(logfc);

Perhaps this could be renamed to something more obviously
associated to fs_context

[]
> diff --git a/fs/fsopen.c b/fs/fsopen.c
[]
> @@ -159,7 +159,57 @@ static ssize_t fscontext_fs_write(struct file *file,
>  	goto err_unlock;
>  }
>  
> +/*
> + * Allow the user to read back any error, warning or informational messages.
> + */
> +static ssize_t fscontext_fs_read(struct file *file,
> +				 char __user *_buf, size_t len, loff_t *pos)
> +{
> +	struct fs_context *fc = file->private_data;
> +	struct fc_log *log = fc->log;
> +	struct inode *inode = file_inode(file);
> +	unsigned int logsize = ARRAY_SIZE(log->buffer);

logsize isn't modified, could be removed and ARRAY_SIZE used in-place.

> +	ssize_t ret;
> +	char *p;
> +	bool need_free;

It _looks_ like need_free isn't set by all codepaths, but
it doesn't need to be given the goto/return.

It also seems the the logic isn't straightforward here and
could be rewritten to be more obvious.

> +	int index, n;
> +
> +	ret = inode_lock_killable(inode);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = -ENODATA;
> +	if (log->head != log->tail) {
> +		index = log->tail & (logsize - 1);
> +		p = log->buffer[index];
> +		need_free = log->need_free & (1 << index);
> +		log->buffer[index] = NULL;
> +		log->need_free &= ~(1 << index);
> +		log->tail++;
> +		ret = 0;
> +	}
> +
> +	inode_unlock(inode);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = -EMSGSIZE;
> +	n = strlen(p);
> +	if (n > len)
> +		goto err_free;
> +	ret = -EFAULT;
> +	if (copy_to_user(_buf, p, n) != 0)
> +		goto err_free;
> +	ret = n;
> +
> +err_free:
> +	if (need_free)
> +		kfree(p);
> +	return ret;
> +}
> +
>  const struct file_operations fscontext_fs_fops = {
> +	.read		= fscontext_fs_read,
>  	.write		= fscontext_fs_write,
>  	.release	= fscontext_fs_release,
>  	.llseek		= no_llseek,
> @@ -330,6 +380,7 @@ SYSCALL_DEFINE5(fsopen, const char __user *, _fs_name, unsigned int, flags,
>  	struct file_system_type *fs_type;
>  	struct fs_context *fc;
>  	const char *fs_name;
> +	int ret;
>  
>  	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
>  		return -EPERM;
> @@ -353,7 +404,18 @@ SYSCALL_DEFINE5(fsopen, const char __user *, _fs_name, unsigned int, flags,
>  
>  	fc->phase = FS_CONTEXT_CREATE_PARAMS;
>  
> +	ret = -ENOMEM;
> +	fc->log = kzalloc(sizeof(*fc->log), GFP_KERNEL);
> +	if (!fc->log)
> +		goto err_fc;
> +	refcount_set(&fc->log->usage, 1);
> +	fc->log->owner = fs_type->owner;
> +
>  	return fsopen_create_fd(fc, flags & FSOPEN_CLOEXEC);
> +
> +err_fc:
> +	put_fs_context(fc);
> +	return ret;
>  }

[]

> diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
> 
> @@ -117,4 +119,60 @@ extern int vfs_get_super(struct fs_context *fc,
>  
>  extern const struct file_operations fscontext_fs_fops;
>  
> +/*
> + * Mount error, warning and informational message logging.  This structure is
> + * shareable between a mount and a subordinate mount.
> + */
> +struct fc_log {
> +	refcount_t	usage;
> +	u8		head;		/* Insertion index in buffer[] */
> +	u8		tail;		/* Removal index in buffer[] */
> +	u8		need_free;	/* Mask of kfree'able items in buffer[] */
> +	struct module	*owner;		/* Owner module for strings that don't then need freeing */
> +	char		*buffer[8];
> +};
> +
> +extern __attribute__((format(printf, 2, 3)))

__printf(2, 3)

> +void logfc(struct fs_context *fc, const char *fmt, ...);
> +
> +/**
> + * infof - Store supplementary informational message
> + * @fc: The context in which to log the informational message
> + * @fmt: The format string
> + *
> + * Store the supplementary informational message for the process if the process
> + * has enabled the facility.
> + */
> +#define infof(fc, fmt, ...) ({ logfc(fc, "i "fmt, ## __VA_ARGS__); })

Why a statement expression macro and not just

#define infof(fc, fmt, ...)	logfc(fc, "i " fmt, ##__VA_ARGS__)

etc...




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux