1#!/bin/sh 2 3# 4# Copyright (c) 2013 Peter Holm <pho@FreeBSD.org> 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29# Test scenario by Nate Eldredge neldredge math ucsdnedu 30 31# kern/127213: [tmpfs] sendfile on tmpfs data corruption 32 33[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 34 35. ../default.cfg 36 37set -e 38 39odir=`pwd` 40cd /tmp 41cat > server.c <<EOF 42#include <stdio.h> 43#include <fcntl.h> 44#include <unistd.h> 45#include <stdlib.h> 46#include "util.h" 47 48int main(int argc, char *argv[]) { 49 int f, listener, connection; 50 if (argc < 3) { 51 fprintf(stderr, "Usage: %s filename socketname\n", argv[0]); 52 exit(1); 53 } 54 if ((f = open(argv[1], O_RDONLY)) < 0) { 55 perror(argv[1]); 56 exit(1); 57 } 58 if ((listener = listen_unix_socket(argv[2])) < 0) { 59 exit(1); 60 } 61 if ((connection = accept_unix_socket(listener)) >= 0) { 62 real_sendfile(f, connection); 63 } 64 return 0; 65} 66EOF 67cat > client.c <<EOF 68#include <stdio.h> 69#include <fcntl.h> 70#include <unistd.h> 71#include <stdlib.h> 72#include "util.h" 73 74int main(int argc, char *argv[]) { 75 int s; 76 if (argc < 2) { 77 fprintf(stderr, "Usage: %s socketname\n", argv[0]); 78 exit(1); 79 } 80 if ((s = connect_unix_socket(argv[1])) < 0) { 81 exit(1); 82 } 83 fake_sendfile(s, 1); 84 return 0; 85} 86EOF 87 88cat > util.c <<EOF 89/* send data from file to unix domain socket */ 90 91#include <stdio.h> 92#include <time.h> 93#include <signal.h> 94#include <errno.h> 95#include <sys/types.h> 96#include <sys/socket.h> 97#include <sys/un.h> 98#include <string.h> 99#include <stdlib.h> 100#include <unistd.h> 101 102int create_unix_socket(void) { 103 int fd; 104 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 105 perror("socket"); 106 return -1; 107 } 108 return fd; 109} 110 111int make_unix_sockaddr(const char *pathname, struct sockaddr_un *sa) { 112 memset(sa, 0, sizeof(*sa)); 113 sa->sun_family = PF_LOCAL; 114 if (strlen(pathname) + 1 > sizeof(sa->sun_path)) { 115// fprintf(stderr, "%s: pathname too long (max %lu)\n", 116// pathname, sizeof(sa->sun_path)); 117 errno = ENAMETOOLONG; 118 return -1; 119 } 120 strcpy(sa->sun_path, pathname); 121 return 0; 122} 123 124static char *sockname; 125void delete_socket(void) { 126 unlink(sockname); 127} 128 129int listen_unix_socket(const char *path) { 130 int fd; 131 struct sockaddr_un sa; 132 if (make_unix_sockaddr(path, &sa) < 0) 133 return -1; 134 if ((fd = create_unix_socket()) < 0) 135 return -1; 136 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 137 perror("bind"); 138 close(fd); 139 return -1; 140 } 141 sockname = strdup(path); 142 atexit(delete_socket); 143 144 if (listen(fd, 5) < 0) { 145 perror("listen"); 146 close(fd); 147 return -1; 148 } 149 return fd; 150} 151 152int accept_unix_socket(int fd) { 153 int s; 154 if ((s = accept(fd, NULL, 0)) < 0) { 155 perror("accept"); 156 return -1; 157 } 158 return s; 159} 160 161int connect_unix_socket(const char *path) { 162 int fd; 163 struct sockaddr_un sa; 164 if (make_unix_sockaddr(path, &sa) < 0) 165 return -1; 166 if ((fd = create_unix_socket()) < 0) 167 return -1; 168 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 169 perror("connect"); 170 return -1; 171 } 172 return fd; 173} 174 175#define BUFSIZE 65536 176 177int fake_sendfile(int from, int to) { 178 char buf[BUFSIZE]; 179 int v; 180 int sent = 0; 181 while ((v = read(from, buf, BUFSIZE)) > 0) { 182 int d = 0; 183 while (d < v) { 184 int w = write(to, buf, v - d); 185 if (w <= 0) { 186 perror("write"); 187 return -1; 188 } 189 d += w; 190 sent += w; 191 } 192 } 193 if (v != 0) { 194 perror("read"); 195 return -1; 196 } 197 return sent; 198} 199 200int real_sendfile(int from, int to) { 201 int v; 202 v = sendfile(from, to, 0, 0, NULL, NULL, 0); 203 if (v < 0) { 204 perror("sendfile"); 205 } 206 return v; 207} 208EOF 209 210cat > util.h <<EOF 211/* send data from file to unix domain socket */ 212 213#include <stdio.h> 214#include <time.h> 215#include <signal.h> 216#include <errno.h> 217#include <sys/types.h> 218#include <sys/socket.h> 219#include <sys/un.h> 220 221int create_unix_socket(void); 222int make_unix_sockaddr(const char *pathname, struct sockaddr_un *sa); 223int listen_unix_socket(const char *path); 224int accept_unix_socket(int fd); 225int connect_unix_socket(const char *path); 226int fake_sendfile(int from, int to); 227int real_sendfile(int from, int to); 228EOF 229 230mycc -c -Wall -Wextra -O2 util.c 231mycc -o server -Wall -Wextra -O2 server.c util.o 232mycc -o client -Wall -Wextra -O2 client.c util.o 233rm -f server.c client.c util.c util.o util.h mysocket 234 235mount | grep "$mntpoint" | grep -q tmpfs && umount $mntpoint 236mount -t tmpfs tmpfs $mntpoint 237 238dd if=/dev/random of=$mntpoint/data bs=123456 count=1 status=none 239./server $mntpoint/data mysocket & 240sleep 0.2 241./client mysocket > data.$$ 242cmp $mntpoint/data data.$$ || 243 { echo "FAIL Data mismatch"; ls -l $mntpoint/data data.$$; } 244rm -f data.$$ server client mysocket 245 246wait 247while mount | grep "$mntpoint" | grep -q tmpfs; do 248 umount $mntpoint || sleep 1 249done 250