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...

Reply via email to