1 2#include "network_backends.h" 3 4#include "network.h" 5#include "log.h" 6#include "sys-mmap.h" 7 8#include <setjmp.h> 9#include <signal.h> 10#include <unistd.h> 11 12#include <errno.h> 13#include <string.h> 14 15#define MMAP_CHUNK_SIZE (512*1024) 16 17#ifdef HAVE_LIBSMBCLIENT_H 18#include <libsmbclient.h> 19#endif 20 21#define DBE 0 22 23off_t mmap_align_offset(off_t start) { 24 static long pagesize = 0; 25 if (0 == pagesize) { 26 pagesize = sysconf(_SC_PAGESIZE); 27 force_assert(pagesize < MMAP_CHUNK_SIZE); 28 } 29 force_assert(start >= (start % pagesize)); 30 return start - (start % pagesize); 31} 32 33#if defined(USE_MMAP) 34 35static volatile int sigbus_jmp_valid; 36static sigjmp_buf sigbus_jmp; 37 38static void sigbus_handler(int sig) { 39 UNUSED(sig); 40 if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); 41 log_failed_assert(__FILE__, __LINE__, "SIGBUS"); 42} 43 44#if 0 45/* read mmap()ed data into local buffer */ 46#define LOCAL_BUFFERING 1 47#endif 48 49int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { 50 chunk* const c = cq->first; 51 off_t offset, toSend, file_end; 52 ssize_t r; 53 size_t mmap_offset, mmap_avail; 54 const char *data; 55 56 force_assert(NULL != c); 57 force_assert(FILE_CHUNK == c->type); 58 force_assert(c->offset >= 0 && c->offset <= c->file.length); 59 60 offset = c->file.start + c->offset; 61 toSend = c->file.length - c->offset; 62 if (toSend > *p_max_bytes) toSend = *p_max_bytes; 63 file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ 64 65 if (0 == toSend) { 66 chunkqueue_remove_finished_chunks(cq); 67 return 0; 68 } 69 70 if (0 != network_open_file_chunk(srv, con, cq)) return -1; 71 72 /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */ 73 if (0 != sigsetjmp(sigbus_jmp, 1)) { 74 sigbus_jmp_valid = 0; 75 76 log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", 77 c->file.name, c->file.fd); 78 79 munmap(c->file.mmap.start, c->file.mmap.length); 80 c->file.mmap.start = MAP_FAILED; 81#ifdef LOCAL_BUFFERING 82 buffer_reset(c->mem); 83#endif 84 85 return -1; 86 } 87 88 signal(SIGBUS, sigbus_handler); 89 90 /* mmap the buffer if offset is outside old mmap area or not mapped at all */ 91 if (MAP_FAILED == c->file.mmap.start 92 || offset < c->file.mmap.offset 93 || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) { 94 95 if (MAP_FAILED != c->file.mmap.start) { 96 munmap(c->file.mmap.start, c->file.mmap.length); 97 c->file.mmap.start = MAP_FAILED; 98 } 99 100 /* Optimizations for the future: 101 * 102 * adaptive mem-mapping 103 * the problem: 104 * we mmap() the whole file. If someone has alot large files and 32bit 105 * machine the virtual address area will be unrun and we will have a failing 106 * mmap() call. 107 * solution: 108 * only mmap 16M in one chunk and move the window as soon as we have finished 109 * the first 8M 110 * 111 * read-ahead buffering 112 * the problem: 113 * sending out several large files in parallel trashes the read-ahead of the 114 * kernel leading to long wait-for-seek times. 115 * solutions: (increasing complexity) 116 * 1. use madvise 117 * 2. use a internal read-ahead buffer in the chunk-structure 118 * 3. use non-blocking IO for file-transfers 119 * */ 120 121 c->file.mmap.offset = mmap_align_offset(offset); 122 123 /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */ 124 c->file.mmap.length = MMAP_CHUNK_SIZE; 125 if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) { 126 c->file.mmap.length = file_end - c->file.mmap.offset; 127 } 128 129 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { 130 log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:", 131 strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length); 132 return -1; 133 } 134 135#if defined(LOCAL_BUFFERING) 136 sigbus_jmp_valid = 1; 137 buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); 138 sigbus_jmp_valid = 0; 139#else 140# if defined(HAVE_MADVISE) 141 /* don't advise files < 64Kb */ 142 if (c->file.mmap.length > (64*1024)) { 143 /* darwin 7 is returning EINVAL all the time and I don't know how to 144 * detect this at runtime. 145 * 146 * ignore the return value for now */ 147 madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); 148 } 149# endif 150#endif 151 } 152 153 force_assert(offset >= c->file.mmap.offset); 154 mmap_offset = offset - c->file.mmap.offset; 155 force_assert(c->file.mmap.length > mmap_offset); 156 mmap_avail = c->file.mmap.length - mmap_offset; 157 if (toSend > (off_t) mmap_avail) toSend = mmap_avail; 158 159#if defined(LOCAL_BUFFERING) 160 data = c->mem->ptr + mmap_offset; 161#else 162 data = c->file.mmap.start + mmap_offset; 163#endif 164 165 sigbus_jmp_valid = 1; 166#if defined(__WIN32) 167 r = send(fd, data, toSend, 0); 168#else /* __WIN32 */ 169 r = write(fd, data, toSend); 170#endif /* __WIN32 */ 171 sigbus_jmp_valid = 0; 172 173#if defined(__WIN32) 174 if (r < 0) { 175 int lastError = WSAGetLastError(); 176 switch (lastError) { 177 case WSAEINTR: 178 case WSAEWOULDBLOCK: 179 break; 180 case WSAECONNRESET: 181 case WSAETIMEDOUT: 182 case WSAECONNABORTED: 183 return -2; 184 default: 185 log_error_write(srv, __FILE__, __LINE__, "sdd", 186 "send failed: ", lastError, fd); 187 return -1; 188 } 189 } 190#else /* __WIN32 */ 191 if (r < 0) { 192 switch (errno) { 193 case EAGAIN: 194 case EINTR: 195 break; 196 case EPIPE: 197 case ECONNRESET: 198 return -2; 199 default: 200 log_error_write(srv, __FILE__, __LINE__, "ssd", 201 "write failed:", strerror(errno), fd); 202 return -1; 203 } 204 } 205#endif /* __WIN32 */ 206 207 if (r >= 0) { 208 *p_max_bytes -= r; 209 chunkqueue_mark_written(cq, r); 210 } 211 212 return (r > 0 && r == toSend) ? 0 : -3; 213} 214 215#endif /* USE_MMAP */ 216 217int network_write_smbfile_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { 218 219 220 chunk* const c = cq->first; 221 ssize_t r; 222 off_t offset; 223 off_t toSend; 224 stat_cache_entry *sce = NULL; 225 //off_t buffer_size = 256*1024; 226 off_t buffer_size = 512*1024; 227 char* buff = NULL; 228#ifdef HAVE_LIBSMBCLIENT_H 229 230 force_assert(NULL != c); 231 force_assert(SMB_CHUNK == c->type); 232 force_assert(c->offset >= 0 && c->offset <= c->file.length); 233 234 Cdbg(DBE, "*****************************************"); 235 236 offset = c->file.start + c->offset; 237 toSend = ( c->file.length - c->offset > buffer_size) ? buffer_size : c->file.length - c->offset; 238 239 if (toSend > *p_max_bytes) toSend = *p_max_bytes; 240 241 if (0 == toSend) { 242 chunkqueue_remove_finished_chunks(cq); 243 return 0; 244 } 245 246 if (0 != network_open_file_chunk(srv, con, cq)) return -1; 247 248 buff = malloc(buffer_size); 249 250 if(buff==NULL){ 251 Cdbg(DBE, "malloc fail"); 252 chunkqueue_remove_finished_chunks(cq); 253 return 0; 254 } 255 256 memset(buff, '\0', buffer_size); 257 258 Cdbg(DBE, "size of off_t=%d", sizeof(off_t)); 259 Cdbg(DBE, "c->file.fd=[%d], c->file.length=[%lld]", c->file.fd, c->file.length); 260 261 smbc_wrapper_lseek(con, c->file.fd, offset, SEEK_SET ); 262 263 Cdbg(DBE, "offset=[%lld], toSend=[%lld]", offset, toSend); 264 r = smbc_wrapper_read(con, c->file.fd, buff, (size_t)toSend); 265 266 Cdbg(DBE, "read complete, r=[%lld]", r); 267 268 if( toSend == -1 || r < 0 ) { 269 270 free(buff); 271 272 smbc_wrapper_close(con, c->file.fd); 273 274 switch (errno) { 275 case EAGAIN: 276 case EINTR: 277 break; 278 case EPIPE: 279 case ECONNRESET: 280 return -2; 281 default: 282 log_error_write(srv, __FILE__, __LINE__, "ssd", 283 "sendfile failed:", strerror(errno), fd); 284 return -1; 285 } 286 } 287 else if (r == 0) { 288 289 free(buff); 290 291 int oerrno = errno; 292 /* We got an event to write but we wrote nothing 293 * 294 * - the file shrinked -> error 295 * - the remote side closed inbetween -> remote-close */ 296 297 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { 298 /* file is gone ? */ 299 return -1; 300 } 301 302 if (offset > sce->st.st_size) { 303 /* file shrinked, close the connection */ 304 errno = oerrno; 305 306 return -1; 307 } 308 309 errno = oerrno; 310 return -2; 311 } 312 313 r = write(fd, buff, toSend); 314 315 free(buff); 316 317 if (r >= 0) { 318 chunkqueue_mark_written(cq, r); 319 *p_max_bytes -= r; 320 } 321#endif 322 323 return (r > 0 && r == toSend) ? 0 : -3; 324} 325 326