📄 sys_parisc32.c
字号:
vector++; ivp++; i--; } inode = file->f_dentry->d_inode; /* VERIFY_WRITE actually means a read, as we write to user space */ retval = locks_verify_area((type == VERIFY_WRITE ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), inode, file, file->f_pos, tot_len); if (retval) { if (iov != iovstack) kfree(iov); return retval; } /* Then do the actual IO. Note that sockets need to be handled * specially as they have atomicity guarantees and can handle * iovec's natively */ if (inode->i_sock) { int err; err = sock_readv_writev(type, inode, file, iov, count, tot_len); if (iov != iovstack) kfree(iov); return err; } if (!file->f_op) { if (iov != iovstack) kfree(iov); return -EINVAL; } /* VERIFY_WRITE actually means a read, as we write to user space */ fn = file->f_op->read; if (type == VERIFY_READ) fn = (IO_fn_t) file->f_op->write; ivp = iov; while (count > 0) { void * base; int len, nr; base = ivp->iov_base; len = ivp->iov_len; ivp++; count--; nr = fn(file, base, len, &file->f_pos); if (nr < 0) { if (retval) break; retval = nr; break; } retval += nr; if (nr != len) break; } if (iov != iovstack) kfree(iov); return retval;}asmlinkage longsys32_readv(int fd, struct iovec32 *vector, u32 count){ struct file *file; ssize_t ret; ret = -EBADF; file = fget(fd); if (!file) goto bad_file; if (file->f_op && (file->f_mode & FMODE_READ) && (file->f_op->readv || file->f_op->read)) ret = do_readv_writev32(VERIFY_WRITE, file, vector, count); fput(file);bad_file: return ret;}asmlinkage longsys32_writev(int fd, struct iovec32 *vector, u32 count){ struct file *file; ssize_t ret; ret = -EBADF; file = fget(fd); if(!file) goto bad_file; if (file->f_op && (file->f_mode & FMODE_WRITE) && (file->f_op->writev || file->f_op->write)) ret = do_readv_writev32(VERIFY_READ, file, vector, count); fput(file);bad_file: return ret;}/********** Borrowed from sparc64 -- hardly reviewed, not tested *****/#include <net/scm.h>/* XXX This really belongs in some header file... -DaveM */#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, 24 for IPv6, about 80 for AX.25 */extern struct socket *sockfd_lookup(int fd, int *err);/* XXX This as well... */extern __inline__ void sockfd_put(struct socket *sock){ fput(sock->file);}struct msghdr32 { u32 msg_name; int msg_namelen; u32 msg_iov; __kernel_size_t32 msg_iovlen; u32 msg_control; __kernel_size_t32 msg_controllen; unsigned msg_flags;};struct cmsghdr32 { __kernel_size_t32 cmsg_len; int cmsg_level; int cmsg_type;};/* Bleech... */#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen))#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen))#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) )#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32))))#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len))#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len))#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ (struct cmsghdr32 *)(ctl) : \ (struct cmsghdr32 *)NULL)#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, struct cmsghdr32 *__cmsg, int __cmsg_len){ struct cmsghdr32 * __ptr; __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + CMSG32_ALIGN(__cmsg_len)); if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) return NULL; return __ptr;}__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, struct cmsghdr32 *__cmsg, int __cmsg_len){ return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, __cmsg, __cmsg_len);}static inline int iov_from_user32_to_kern(struct iovec *kiov, struct iovec32 *uiov32, int niov){ int tot_len = 0; while(niov > 0) { u32 len, buf; if(get_user(len, &uiov32->iov_len) || get_user(buf, &uiov32->iov_base)) { tot_len = -EFAULT; break; } tot_len += len; kiov->iov_base = (void *)A(buf); kiov->iov_len = (__kernel_size_t) len; uiov32++; kiov++; niov--; } return tot_len;}static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, struct msghdr32 *umsg){ u32 tmp1, tmp2, tmp3; int err; err = get_user(tmp1, &umsg->msg_name); err |= __get_user(tmp2, &umsg->msg_iov); err |= __get_user(tmp3, &umsg->msg_control); if (err) return -EFAULT; kmsg->msg_name = (void *)A(tmp1); kmsg->msg_iov = (struct iovec *)A(tmp2); kmsg->msg_control = (void *)A(tmp3); err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); err |= get_user(kmsg->msg_flags, &umsg->msg_flags); return err;}/* I've named the args so it is easy to tell whose space the pointers are in. */static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, char *kern_address, int mode){ int tot_len; if(kern_msg->msg_namelen) { if(mode==VERIFY_READ) { int err = move_addr_to_kernel(kern_msg->msg_name, kern_msg->msg_namelen, kern_address); if(err < 0) return err; } kern_msg->msg_name = kern_address; } else kern_msg->msg_name = NULL; if(kern_msg->msg_iovlen > UIO_FASTIOV) { kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), GFP_KERNEL); if(!kern_iov) return -ENOMEM; } tot_len = iov_from_user32_to_kern(kern_iov, (struct iovec32 *)kern_msg->msg_iov, kern_msg->msg_iovlen); if(tot_len >= 0) kern_msg->msg_iov = kern_iov; else if(kern_msg->msg_iovlen > UIO_FASTIOV) kfree(kern_iov); return tot_len;}/* There is a lot of hair here because the alignment rules (and * thus placement) of cmsg headers and length are different for * 32-bit apps. -DaveM */static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, unsigned char *stackbuf, int stackbuf_size){ struct cmsghdr32 *ucmsg; struct cmsghdr *kcmsg, *kcmsg_base; __kernel_size_t32 ucmlen; __kernel_size_t kcmlen, tmp; kcmlen = 0; kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; ucmsg = CMSG32_FIRSTHDR(kmsg); while(ucmsg != NULL) { if(get_user(ucmlen, &ucmsg->cmsg_len)) return -EFAULT; /* Catch bogons. */ if(CMSG32_ALIGN(ucmlen) < CMSG32_ALIGN(sizeof(struct cmsghdr32))) return -EINVAL; if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + ucmlen) > kmsg->msg_controllen) return -EINVAL; tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); kcmlen += tmp; ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); } if(kcmlen == 0) return -EINVAL; /* The kcmlen holds the 64-bit version of the control length. * It may not be modified as we do not stick it into the kmsg * until we have successfully copied over all of the data * from the user. */ if(kcmlen > stackbuf_size) kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); if(kcmsg == NULL) return -ENOBUFS; /* Now copy them over neatly. */ memset(kcmsg, 0, kcmlen); ucmsg = CMSG32_FIRSTHDR(kmsg); while(ucmsg != NULL) { __get_user(ucmlen, &ucmsg->cmsg_len); tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); kcmsg->cmsg_len = tmp; __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); /* Copy over the data. */ if(copy_from_user(CMSG_DATA(kcmsg), CMSG32_DATA(ucmsg), (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) goto out_free_efault; /* Advance. */ kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); } /* Ok, looks like we made it. Hook it up and return success. */ kmsg->msg_control = kcmsg_base; kmsg->msg_controllen = kcmlen; return 0;out_free_efault: if(kcmsg_base != (struct cmsghdr *)stackbuf) kfree(kcmsg_base); return -EFAULT;}static void put_cmsg32(struct msghdr *kmsg, int level, int type, int len, void *data){ struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; struct cmsghdr32 cmhdr; int cmlen = CMSG32_LEN(len); if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { kmsg->msg_flags |= MSG_CTRUNC; return; } if(kmsg->msg_controllen < cmlen) { kmsg->msg_flags |= MSG_CTRUNC; cmlen = kmsg->msg_controllen; } cmhdr.cmsg_level = level; cmhdr.cmsg_type = type; cmhdr.cmsg_len = cmlen; if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) return; if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) return; cmlen = CMSG32_SPACE(len); kmsg->msg_control += cmlen; kmsg->msg_controllen -= cmlen;}static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm){ struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); int fdnum = scm->fp->count; struct file **fp = scm->fp->fp; int *cmfptr; int err = 0, i; if (fdnum < fdmax) fdmax = fdnum; for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { int new_fd; err = get_unused_fd(); if (err < 0) break; new_fd = err; err = put_user(new_fd, cmfptr); if (err) { put_unused_fd(new_fd); break; } /* Bump the usage count and install the file. */ get_file(fp[i]); fd_install(new_fd, fp[i]); } if (i > 0) { int cmlen = CMSG32_LEN(i * sizeof(int)); if (!err) err = put_user(SOL_SOCKET, &cm->cmsg_level); if (!err) err = put_user(SCM_RIGHTS, &cm->cmsg_type); if (!err) err = put_user(cmlen, &cm->cmsg_len); if (!err) { cmlen = CMSG32_SPACE(i * sizeof(int)); kmsg->msg_control += cmlen; kmsg->msg_controllen -= cmlen; } } if (i < fdnum) kmsg->msg_flags |= MSG_CTRUNC; /* * All of the files that fit in the message have had their * usage counts incremented, so we just free the list. */ __scm_destroy(scm);}/* In these cases we (currently) can just copy to data over verbatim * because all CMSGs created by the kernel have well defined types which * have the same layout in both the 32-bit and 64-bit API. One must add * some special cased conversions here if we start sending control messages * with incompatible types. * * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after * we do our work. The remaining cases are: * * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean * IP_TTL int 32-bit clean * IP_TOS __u8 32-bit clean * IP_RECVOPTS variable length 32-bit clean * IP_RETOPTS variable length 32-bit clean * (these last two are clean because the types are defined * by the IPv4 protocol) * IP_RECVERR struct sock_extended_err + * struct sockaddr_in 32-bit clean * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + * struct sockaddr_in6 32-bit clean * IPV6_PKTINFO struct in6_pktinfo 32-bit clean * IPV6_HOPLIMIT int 32-bit clean * IPV6_FLOWINFO u32 32-bit clean * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean * IPV6_RTHDR ipv6 routing exthdr 32-bit clean * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean */static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr){ unsigned char *workbuf, *wp; unsigned long bufsz, space_avail; struct cmsghdr *ucmsg; bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; space_avail = kmsg->msg_controllen + bufsz; wp = workbuf = kmalloc(bufsz, GFP_KERNEL); if(workbuf == NULL) goto fail; /* To make this more sane we assume the kernel sends back properly * formatted control messages. Because of how the kernel will truncate * the cmsg_len for MSG_TRUNC cases, we need not check that case either. */ ucmsg = (struct cmsghdr *) orig_cmsg_uptr; while(((unsigned long)ucmsg) <= (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; int clen64, clen32; /* UCMSG is the 64-bit format CMSG entry in user-space. * KCMSG32 is within the kernel space temporary buffer * we use to convert into a 32-bit style CMSG. */ __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); clen64 = kcmsg32->cmsg_len; copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), clen64 - CMSG_ALIGN(sizeof(*ucmsg))); clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + CMSG32_ALIGN(sizeof(struct cmsghdr32))); kcmsg32->cmsg_len = clen32; ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); } /* Copy back fixed up data, and adjust pointers. */ bufsz = (wp - workbuf); copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); kmsg->msg_control = (struct cmsghdr *) (((char *)orig_cmsg_uptr) + bufsz); kmsg->msg_controllen = space_avail - bufsz; kfree(workbuf); return;fail: /* If we leave the 64-bit format CMSG chunks in there, * the application could get confused and crash. So to * ensure greater recovery, we report no CMSGs. */ kmsg->msg_controllen += bufsz; kmsg->msg_control = (void *) orig_cmsg_uptr;}asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags){ struct socket *sock; char address[MAX_SOCK_ADDR]; struct iovec iov[UIO_FASTIOV]; unsigned char ctl[sizeof(struct cmsghdr) + 20]; unsigned char *ctl_buf = ctl; struct msghdr kern_msg; int err, total_len; if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) return -EFAULT; if(kern_msg.msg_iovlen > UIO_MAXIOV) return -EINVAL; err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); if (err < 0) goto out; total_len = err; if(kern_msg.msg_controllen) { err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); if(err) goto out_freeiov; ctl_buf = kern_msg.msg_control; } kern_msg.msg_flags = user_flags; sock = sockfd_lookup(fd, &err); if (sock != NULL) { if (sock->file->f_flags & O_NONBLOCK) kern_msg.msg_flags |= MSG_DONTWAIT; err = sock_sendmsg(sock, &kern_msg, total_len); sockfd_put(sock); } /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ if(ctl_buf != ctl) kfree(ctl_buf);out_freeiov: if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov);out: return err;}asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags){ struct iovec iovstack[UIO_FASTIOV]; struct msghdr kern_msg; char addr[MAX_SOCK_ADDR]; struct socket *sock; struct iovec *iov = iovstack; struct sockaddr *uaddr; int *uaddr_len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -