aboutsummaryrefslogtreecommitdiff
path: root/sys/compat
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c2
-rw-r--r--sys/compat/lindebugfs/lindebugfs.c83
-rw-r--r--sys/compat/linux/linux_misc.c1
-rw-r--r--sys/compat/linux/linux_socket.c61
-rw-r--r--sys/compat/linux/linux_socket.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hardirq.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/highmem.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/spinlock.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h34
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c26
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c14
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c48
14 files changed, 212 insertions, 97 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 1064987c3abf..4ec6dd452b32 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -757,7 +757,7 @@ static int
freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count)
{
struct freebsd32_kevent_args *uap;
- struct kevent32 ks32[KQ_NEVENTS];
+ struct kevent32 ks32[KQ_NEVENTS] = {};
int i, error;
KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count));
diff --git a/sys/compat/lindebugfs/lindebugfs.c b/sys/compat/lindebugfs/lindebugfs.c
index 857546f61e55..88b92afd374a 100644
--- a/sys/compat/lindebugfs/lindebugfs.c
+++ b/sys/compat/lindebugfs/lindebugfs.c
@@ -117,9 +117,14 @@ debugfs_fill(PFS_FILL_ARGS)
struct dentry_meta *d;
struct linux_file lf = {};
struct vnode vn;
- char *buf;
- int rc;
- off_t off = 0;
+ struct iovec *iov;
+ size_t cnt, orig_resid;
+ ssize_t rc;
+ off_t off;
+
+ /* Linux file operations assume a pointer to a user buffer. */
+ if (uio->uio_segflg != UIO_USERSPACE)
+ return (EOPNOTSUPP);
if ((rc = linux_set_current_flags(curthread, M_NOWAIT)))
return (rc);
@@ -130,42 +135,64 @@ debugfs_fill(PFS_FILL_ARGS)
rc = d->dm_fops->open(&vn, &lf);
if (rc < 0) {
#ifdef INVARIANTS
- printf("%s:%d open failed with %d\n", __func__, __LINE__, rc);
+ printf("%s:%d open failed with %zd\n", __func__, __LINE__, rc);
#endif
return (-rc);
}
- rc = -ENODEV;
- switch (uio->uio_rw) {
- case UIO_READ:
- if (d->dm_fops->read != NULL) {
- rc = -ENOMEM;
- buf = malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT);
- if (buf != NULL) {
- rc = d->dm_fops->read(&lf, buf, sb->s_size,
- &off);
- if (rc > 0)
- sbuf_bcpy(sb, buf, strlen(buf));
-
- free(buf, M_DFSINT);
- }
+ off = uio->uio_offset;
+ orig_resid = uio->uio_resid;
+ while (uio->uio_resid > 0) {
+ KASSERT(uio->uio_iovcnt > 0,
+ ("%s: uio %p iovcnt underflow", __func__, uio));
+
+ iov = uio->uio_iov;
+ cnt = iov->iov_len;
+ if (cnt == 0) {
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ continue;
}
- break;
- case UIO_WRITE:
- if (d->dm_fops->write != NULL) {
- sbuf_finish(sb);
- rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb),
- &off);
+ if (cnt > uio->uio_resid)
+ cnt = uio->uio_resid;
+
+ switch (uio->uio_rw) {
+ case UIO_READ:
+ if (d->dm_fops->read != NULL)
+ rc = d->dm_fops->read(&lf, iov->iov_base, cnt,
+ &off);
+ else
+ rc = -ENODEV;
+ break;
+ case UIO_WRITE:
+ if (d->dm_fops->write != NULL)
+ rc = d->dm_fops->write(&lf, iov->iov_base, cnt,
+ &off);
+ else
+ rc = -ENODEV;
+ break;
}
- break;
+
+ if (rc <= 0)
+ break;
+
+ iov->iov_base = (char *)iov->iov_base + rc;
+ iov->iov_len -= rc;
+ uio->uio_resid -= rc;
+ uio->uio_offset = off;
}
if (d->dm_fops->release)
d->dm_fops->release(&vn, &lf);
+ /* Return success for short operations. */
+ if (orig_resid != uio->uio_resid)
+ rc = 0;
+
if (rc < 0) {
#ifdef INVARIANTS
- printf("%s:%d read/write failed with %d\n", __func__, __LINE__, rc);
+ printf("%s:%d read/write failed with %zd\n", __func__, __LINE__,
+ rc);
#endif
return (-rc);
}
@@ -207,7 +234,7 @@ debugfs_create_file(const char *name, umode_t mode,
flags = fops->write ? PFS_RDWR : PFS_RD;
pfs_create_file(pnode, &dnode->d_pfs_node, name, debugfs_fill,
- debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT);
+ debugfs_attr, NULL, debugfs_destroy, flags | PFS_RAW | PFS_NOWAIT);
if (dnode->d_pfs_node == NULL) {
free(dm, M_DFSINT);
return (NULL);
@@ -671,7 +698,7 @@ fops_str_read(struct file *filp, char __user *ubuf, size_t read_size,
}
static ssize_t
-fops_str_write(struct file *filp, const char *buf, size_t write_size,
+fops_str_write(struct file *filp, const char __user *buf, size_t write_size,
loff_t *ppos)
{
char *old, *new;
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 69fb9935a9ae..937b010c8435 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -750,6 +750,7 @@ linux_common_wait(struct thread *td, idtype_t idtype, int id, int *statusp,
error = linux_copyout_rusage(&wru.wru_self, rup);
if (error == 0 && infop != NULL && td->td_retval[0] != 0) {
sig = bsd_to_linux_signal(siginfo.si_signo);
+ memset(&lsi, 0, sizeof(lsi));
siginfo_to_lsiginfo(&siginfo, &lsi, sig);
error = copyout(&lsi, infop, sizeof(lsi));
}
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index b1a483ce611c..023be1e6b885 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -594,10 +594,34 @@ linux_to_bsd_tcp_sockopt(int opt)
return (-2);
case LINUX_TCP_MD5SIG:
return (TCP_MD5SIG);
+ case LINUX_TCP_USER_TIMEOUT:
+ return (TCP_MAXUNACKTIME);
}
return (-1);
}
+static u_int
+linux_to_bsd_tcp_user_timeout(l_uint linux_timeout)
+{
+
+ /*
+ * Linux exposes TCP_USER_TIMEOUT in milliseconds while
+ * TCP_MAXUNACKTIME uses whole seconds. Round up partial
+ * seconds so a non-zero Linux timeout never becomes zero.
+ */
+ return (howmany(linux_timeout, 1000U));
+}
+
+static l_uint
+bsd_to_linux_tcp_user_timeout(u_int bsd_timeout)
+{
+
+ if (bsd_timeout > UINT_MAX / 1000U)
+ return (UINT_MAX);
+
+ return (bsd_timeout * 1000U);
+}
+
static int
linux_to_bsd_msg_flags(int flags)
{
@@ -2057,8 +2081,10 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
struct proc *p = td->td_proc;
struct linux_pemuldata *pem;
l_timeval linux_tv;
+ l_uint linux_timeout;
struct sockaddr *sa;
struct timeval tv;
+ u_int bsd_timeout;
socklen_t len;
int error, level, name, val;
@@ -2130,6 +2156,24 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
break;
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(args->optname);
+ switch (name) {
+ case TCP_MAXUNACKTIME:
+ if (args->optlen < sizeof(linux_timeout))
+ return (EINVAL);
+
+ error = copyin(PTRIN(args->optval), &linux_timeout,
+ sizeof(linux_timeout));
+ if (error != 0)
+ return (error);
+
+ bsd_timeout = linux_to_bsd_tcp_user_timeout(
+ linux_timeout);
+ return (kern_setsockopt(td, args->s, level, name,
+ &bsd_timeout, UIO_SYSSPACE,
+ sizeof(bsd_timeout)));
+ default:
+ break;
+ }
break;
case SOL_NETLINK:
name = args->optname;
@@ -2279,10 +2323,12 @@ linux_getsockopt_so_linger(struct thread *td,
int
linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
{
+ l_uint linux_timeout;
l_timeval linux_tv;
struct timeval tv;
socklen_t tv_len, xulen, len;
struct sockaddr *sa;
+ u_int bsd_timeout;
struct xucred xu;
struct l_ucred lxu;
int error, level, name, newval;
@@ -2373,6 +2419,21 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
break;
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(args->optname);
+ switch (name) {
+ case TCP_MAXUNACKTIME:
+ len = sizeof(bsd_timeout);
+ error = kern_getsockopt(td, args->s, level, name,
+ &bsd_timeout, UIO_SYSSPACE, &len);
+ if (error != 0)
+ return (error);
+
+ linux_timeout = bsd_to_linux_tcp_user_timeout(
+ bsd_timeout);
+ return (linux_sockopt_copyout(td, &linux_timeout,
+ sizeof(linux_timeout), args));
+ default:
+ break;
+ }
break;
default:
name = -1;
diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h
index 68176c3cc401..f2a96b3e7dcb 100644
--- a/sys/compat/linux/linux_socket.h
+++ b/sys/compat/linux/linux_socket.h
@@ -322,6 +322,7 @@ int linux_accept(struct thread *td, struct linux_accept_args *args);
#define LINUX_TCP_KEEPCNT 6
#define LINUX_TCP_INFO 11
#define LINUX_TCP_MD5SIG 14
+#define LINUX_TCP_USER_TIMEOUT 18
struct l_ifmap {
l_ulong mem_start;
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index f1568ad6282d..7e28be070850 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -364,27 +364,23 @@ static inline ssize_t
simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos,
void *orig, size_t buf_size)
{
- void *p, *read_pos = ((char *) orig) + *ppos;
+ void *read_pos = ((char *) orig) + *ppos;
size_t buf_remain = buf_size - *ppos;
+ ssize_t num_read;
- if (buf_remain < 0 || buf_remain > buf_size)
- return -EINVAL;
+ if (*ppos >= buf_size || read_size == 0)
+ return (0);
if (read_size > buf_remain)
read_size = buf_remain;
- /*
- * XXX At time of commit only debugfs consumers could be
- * identified. If others will use this function we may
- * have to revise this: normally we would call copy_to_user()
- * here but lindebugfs will return the result and the
- * copyout is done elsewhere for us.
- */
- p = memcpy(dest, read_pos, read_size);
- if (p != NULL)
- *ppos += read_size;
-
- return (read_size);
+ /* copy_to_user returns number of bytes NOT read */
+ num_read = read_size - copy_to_user(dest, read_pos, read_size);
+ if (num_read == 0)
+ return -EFAULT;
+ *ppos += num_read;
+
+ return (num_read);
}
MALLOC_DECLARE(M_LSATTR);
@@ -415,11 +411,13 @@ int simple_attr_open(struct inode *inode, struct file *filp,
int simple_attr_release(struct inode *inode, struct file *filp);
-ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos);
+ssize_t simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos);
-ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos);
+ssize_t simple_attr_write(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos);
-ssize_t simple_attr_write_signed(struct file *filp, const char *buf,
+ssize_t simple_attr_write_signed(struct file *filp, const char __user *buf,
size_t write_size, loff_t *ppos);
#endif /* _LINUXKPI_LINUX_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/hardirq.h b/sys/compat/linuxkpi/common/include/linux/hardirq.h
index f79451dd0d35..c6cbf1a34f14 100644
--- a/sys/compat/linuxkpi/common/include/linux/hardirq.h
+++ b/sys/compat/linuxkpi/common/include/linux/hardirq.h
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
#include <sys/param.h>
#include <sys/bus.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h
index 58a9cdcdf60f..dc1c4fe2f299 100644
--- a/sys/compat/linuxkpi/common/include/linux/highmem.h
+++ b/sys/compat/linuxkpi/common/include/linux/highmem.h
@@ -45,6 +45,7 @@
#include <linux/mm.h>
#include <linux/page.h>
+#include <linux/hardirq.h>
#define PageHighMem(p) (0)
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index 3c7862890c67..786c09bd6a20 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -85,7 +85,7 @@ struct seq_operations {
int (*show) (struct seq_file *m, void *v);
};
-ssize_t seq_read(struct linux_file *, char *, size_t, off_t *);
+ssize_t seq_read(struct linux_file *, char __user *, size_t, off_t *);
int seq_write(struct seq_file *seq, const void *data, size_t len);
void seq_putc(struct seq_file *m, char c);
void seq_puts(struct seq_file *m, const char *str);
diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h
index 63dc343d1461..a786cbab5e13 100644
--- a/sys/compat/linuxkpi/common/include/linux/spinlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h
@@ -41,6 +41,7 @@
#include <linux/rwlock.h>
#include <linux/bottom_half.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
typedef struct mtx spinlock_t;
diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index 470c224a9778..7c8c4e2e32b9 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -43,13 +43,6 @@ struct sysfs_ops {
size_t);
};
-struct attribute_group {
- const char *name;
- mode_t (*is_visible)(struct kobject *,
- struct attribute *, int);
- struct attribute **attrs;
-};
-
struct bin_attribute {
struct attribute attr;
size_t size;
@@ -59,6 +52,14 @@ struct bin_attribute {
struct bin_attribute *, char *, loff_t, size_t);
};
+struct attribute_group {
+ const char *name;
+ mode_t (*is_visible)(struct kobject *,
+ struct attribute *, int);
+ struct attribute **attrs;
+ struct bin_attribute **bin_attrs;
+};
+
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, .store = _store, \
@@ -370,6 +371,7 @@ static inline int
sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
/* Don't create the group node if grp->name is undefined. */
@@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name);
else
oidp = kobj->oidp;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
(*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", "");
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
+ (*bin_attr)->attr.name,
+ CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE,
+ kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", "");
+ }
return (0);
}
@@ -434,14 +444,20 @@ static inline void
sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) {
if (strcmp(oidp->oid_name, grp->name) != 0)
continue;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
sysctl_remove_name(oidp, (*attr)->name, 1, 1);
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1);
+ }
}
}
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 63f92b8afb2b..01347586ef63 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -3215,16 +3215,28 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
-#ifdef LINUXKPI_DEBUG_80211
- /* XXX-BZ KASSERT later; state going down so no action. */
- if (lvif->lvif_bss == NULL)
- ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
- "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ /*
+ * XXX-BZ KASSERT later; state going down so no action in theory
+ * but try to avoid a NULL-pointer derref for now and gracefully
+ * fail for non-debug kernels.
+ */
+ if (lvif->lvif_bss == NULL) {
+ ic_printf(vap->iv_ic, "%s:%d: ERROR: lvif %p vap %p iv_bss %p "
+ "lvif_bss %p lvif_bss->ni %p synched %d; "
+ "expect follow-up problems\n", __func__, __LINE__,
lvif, vap, vap->iv_bss, lvif->lvif_bss,
(lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
lvif->lvif_bss_synched);
-#endif
-
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ /*
+ * This will likely lead to a firmware crash (if there
+ * was not one before already) and need a
+ * ieee80211_restart_hw() but still better than a panic
+ * for users as they can at least recover.
+ */
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
lsta = lvif->lvif_bss;
LKPI_80211_LVIF_UNLOCK(lvif);
KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index 9c06fe27bebe..eae414ea696e 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -40,7 +40,7 @@
MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
ssize_t
-seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
+seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos)
{
struct seq_file *m;
struct sbuf *sbuf;
@@ -49,6 +49,7 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
m = f->private_data;
sbuf = m->buf;
+ sbuf_clear(sbuf);
p = m->op->start(m, ppos);
rc = m->op->show(m, p);
@@ -59,15 +60,8 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
if (rc)
return (rc);
- rc = sbuf_len(sbuf);
- if (*ppos >= rc || size < 1)
- return (-EINVAL);
-
- size = min(rc - *ppos, size);
- memcpy(ubuf, sbuf_data(sbuf) + *ppos, size);
- *ppos += size;
-
- return (size);
+ return (simple_read_from_buffer(ubuf, size, ppos, sbuf_data(sbuf),
+ sbuf_len(sbuf)));
}
int
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
index 8cc9ec7ecbc9..e5514194cb33 100644
--- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c
+++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
@@ -99,7 +99,8 @@ simple_attr_release(struct inode *inode, struct file *filp)
* On failure, negative signed ERRNO
*/
ssize_t
-simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
+simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos)
{
struct simple_attr *sattr;
uint64_t data;
@@ -119,18 +120,9 @@ simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data);
- ret = strlen(prebuf) + 1;
- if (*ppos >= ret || read_size < 1) {
- ret = -EINVAL;
- goto unlock;
- }
-
- read_size = min(ret - *ppos, read_size);
- ret = strscpy(buf, prebuf + *ppos, read_size);
-
/* add 1 for null terminator */
- if (ret > 0)
- ret += 1;
+ ret = simple_read_from_buffer(buf, read_size, ppos, prebuf,
+ strlen(prebuf) + 1);
unlock:
mutex_unlock(&sattr->mutex);
@@ -155,29 +147,38 @@ unlock:
* On failure, negative signed ERRNO
*/
static ssize_t
-simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
- loff_t *ppos, bool is_signed)
+simple_attr_write_common(struct file *filp, const char __user *ubuf,
+ size_t write_size, loff_t *ppos, bool is_signed)
{
struct simple_attr *sattr;
unsigned long long data;
- size_t bufsize;
+ char *buf;
ssize_t ret;
sattr = filp->private_data;
- bufsize = strlen(buf) + 1;
if (sattr->set == NULL)
return (-EFAULT);
- if (*ppos >= bufsize || write_size < 1)
+ if (*ppos != 0 || write_size < 1)
return (-EINVAL);
+ buf = malloc(write_size, M_LSATTR, M_WAITOK);
+ if (copy_from_user(buf, ubuf, write_size) != 0) {
+ free(buf, M_LSATTR);
+ return (-EFAULT);
+ }
+ if (strnlen(buf, write_size) == write_size) {
+ free(buf, M_LSATTR);
+ return (-EINVAL);
+ }
+
mutex_lock(&sattr->mutex);
if (is_signed)
- ret = kstrtoll(buf + *ppos, 0, &data);
+ ret = kstrtoll(buf, 0, &data);
else
- ret = kstrtoull(buf + *ppos, 0, &data);
+ ret = kstrtoull(buf, 0, &data);
if (ret)
goto unlock;
@@ -185,23 +186,24 @@ simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
if (ret)
goto unlock;
- ret = bufsize - *ppos;
+ ret = write_size;
unlock:
mutex_unlock(&sattr->mutex);
+ free(buf, M_LSATTR);
return (ret);
}
ssize_t
-simple_attr_write(struct file *filp, const char *buf, size_t write_size,
+simple_attr_write(struct file *filp, const char __user *buf, size_t write_size,
loff_t *ppos)
{
return (simple_attr_write_common(filp, buf, write_size, ppos, false));
}
ssize_t
-simple_attr_write_signed(struct file *filp, const char *buf, size_t write_size,
- loff_t *ppos)
+simple_attr_write_signed(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos)
{
return (simple_attr_write_common(filp, buf, write_size, ppos, true));
}