|
Network Block Device @PACKAGE_VERSION@
|
00001 #include "config.h" 00002 #include "nbd-debug.h" 00003 00004 #include <nbdsrv.h> 00005 00006 #include <assert.h> 00007 #include <ctype.h> 00008 #include <netdb.h> 00009 #include <stdlib.h> 00010 #include <stdio.h> 00011 #include <string.h> 00012 #include <syslog.h> 00013 #include <unistd.h> 00014 00015 #include <sys/stat.h> 00016 #include <sys/types.h> 00017 #include <sys/socket.h> 00018 #include <treefiles.h> 00019 #include "backend.h" 00020 #ifdef HAVE_SYS_MOUNT_H 00021 #include <sys/mount.h> 00022 #endif 00023 00024 #define LINELEN 256 /**< Size of static buffer used to read the 00025 authorization file (yuck) */ 00026 00027 #include <cliserv.h> 00028 00029 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) { 00030 struct addrinfo *res, *aitmp, hints; 00031 char *masksep; 00032 char privmask[strlen(mask)+1]; 00033 int masklen; 00034 int addrlen = addr->sa_family == AF_INET ? 4 : 16; 00035 00036 assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6); 00037 00038 strcpy(privmask, mask); 00039 00040 memset(&hints, 0, sizeof(hints)); 00041 hints.ai_family = AF_UNSPEC; 00042 hints.ai_flags = AI_NUMERICHOST; 00043 00044 if((masksep = strchr(privmask, '/'))) { 00045 *masksep = '\0'; 00046 masklen = strtol(++masksep, NULL, 10); 00047 } else { 00048 masklen = addrlen * 8; 00049 } 00050 00051 int e; 00052 if((e = getaddrinfo(privmask, NULL, &hints, &res))) { 00053 g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e)); 00054 return false; 00055 } 00056 aitmp = res; 00057 while(res) { 00058 const uint8_t* byte_s; 00059 uint8_t* byte_t; 00060 uint8_t mask = 0; 00061 int len_left = masklen; 00062 /* 00063 This won't work due to v4/v6 family mismatches. (20160906 draht) 00064 00065 if(res->ai_family != addr->sa_family) { 00066 goto next; 00067 } 00068 */ 00069 switch(addr->sa_family) { 00070 case AF_INET: 00071 byte_s = (const uint8_t*)(&(((struct sockaddr_in*)addr)->sin_addr)); 00072 byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr)); 00073 break; 00074 case AF_INET6: 00075 byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr)); 00076 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr)); 00077 break; 00078 } 00079 while(len_left >= 8) { 00080 if(*byte_s != *byte_t) { 00081 goto next; 00082 } 00083 byte_s++; byte_t++; 00084 len_left -= 8; 00085 } 00086 if(len_left) { 00087 mask = getmaskbyte(len_left); 00088 if((*byte_s & mask) != (*byte_t & mask)) { 00089 goto next; 00090 } 00091 } 00092 freeaddrinfo(aitmp); 00093 return true; 00094 next: 00095 res = res->ai_next; 00096 } 00097 freeaddrinfo(aitmp); 00098 return false; 00099 } 00100 00101 uint8_t getmaskbyte(int masklen) { 00102 if(masklen >= 8) { 00103 return 0xFF; 00104 } 00105 uint8_t retval = 0; 00106 for(int i = 7; i + masklen > 7; i--) { 00107 retval |= 1 << i; 00108 } 00109 00110 return retval; 00111 } 00112 00113 int authorized_client(CLIENT *opts) { 00114 FILE *f ; 00115 char line[LINELEN]; 00116 00117 if (opts->server->authname == NULL) { 00118 msg(LOG_INFO, "No authorization file, granting access."); 00119 return 1; 00120 } 00121 00122 if ((f=fopen(opts->server->authname,"r"))==NULL) { 00123 msg(LOG_INFO, "Can't open authorization file %s (%s).", 00124 opts->server->authname, strerror(errno)); 00125 return 1 ; 00126 } 00127 00128 while (fgets(line,LINELEN,f)!=NULL) { 00129 char* pos; 00130 /* Drop comments */ 00131 if((pos = strchr(line, '#'))) { 00132 *pos = '\0'; 00133 } 00134 /* Skip whitespace */ 00135 pos = line; 00136 while((*pos) && isspace(*pos)) { 00137 pos++; 00138 } 00139 /* Skip content-free lines */ 00140 if(!(*pos)) { 00141 continue; 00142 } 00143 if(address_matches(line, (struct sockaddr*)&opts->clientaddr, NULL)) { 00144 fclose(f); 00145 return 1; 00146 } 00147 } 00148 fclose(f); 00149 return 0; 00150 } 00151 00152 /** 00153 * duplicate server 00154 * @param s the old server we want to duplicate 00155 * @return new duplicated server 00156 **/ 00157 SERVER* dup_serve(const SERVER *const s) { 00158 SERVER *serve = NULL; 00159 00160 serve=g_new0(SERVER, 1); 00161 if(serve == NULL) 00162 return NULL; 00163 00164 if(s->exportname) 00165 serve->exportname = g_strdup(s->exportname); 00166 00167 serve->expected_size = s->expected_size; 00168 00169 if(s->listenaddr) 00170 serve->listenaddr = g_strdup(s->listenaddr); 00171 00172 if(s->authname) 00173 serve->authname = g_strdup(s->authname); 00174 00175 serve->flags = s->flags; 00176 serve->virtstyle = s->virtstyle; 00177 serve->cidrlen = s->cidrlen; 00178 00179 if(s->prerun) 00180 serve->prerun = g_strdup(s->prerun); 00181 00182 if(s->postrun) 00183 serve->postrun = g_strdup(s->postrun); 00184 00185 if(s->transactionlog) 00186 serve->transactionlog = g_strdup(s->transactionlog); 00187 00188 if(s->servename) 00189 serve->servename = g_strdup(s->servename); 00190 00191 if(s->cowdir) 00192 serve->cowdir = g_strdup(s->cowdir); 00193 00194 serve->max_connections = s->max_connections; 00195 00196 return serve; 00197 } 00198 00199 uint64_t size_autodetect(int fhandle) { 00200 off_t es; 00201 u64 bytes __attribute__((unused)); 00202 struct stat stat_buf; 00203 int error; 00204 00205 #ifdef HAVE_SYS_MOUNT_H 00206 #ifdef HAVE_SYS_IOCTL_H 00207 #ifdef BLKGETSIZE64 00208 DEBUG("looking for export size with ioctl BLKGETSIZE64\n"); 00209 if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) { 00210 return bytes; 00211 } 00212 #endif /* BLKGETSIZE64 */ 00213 #endif /* HAVE_SYS_IOCTL_H */ 00214 #endif /* HAVE_SYS_MOUNT_H */ 00215 00216 DEBUG("looking for fhandle size with fstat\n"); 00217 stat_buf.st_size = 0; 00218 error = fstat(fhandle, &stat_buf); 00219 if (!error) { 00220 /* always believe stat if a regular file as it might really 00221 * be zero length */ 00222 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0)) 00223 return (uint64_t)stat_buf.st_size; 00224 } else { 00225 DEBUG("fstat failed: %s", strerror(errno)); 00226 } 00227 00228 DEBUG("looking for fhandle size with lseek SEEK_END\n"); 00229 es = lseek(fhandle, (off_t)0, SEEK_END); 00230 if (es > ((off_t)0)) { 00231 return (uint64_t)es; 00232 } else { 00233 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4))); 00234 } 00235 00236 DEBUG("Could not find size of exported block device: %s", strerror(errno)); 00237 return UINT64_MAX; 00238 } 00239 00240 int exptrim(struct nbd_request* req, CLIENT* client) { 00241 /* Don't trim when we're read only */ 00242 if(client->server->flags & F_READONLY) { 00243 errno = EINVAL; 00244 return -1; 00245 } 00246 /* Don't trim beyond the size of the device, please */ 00247 if(req->from + req->len > client->exportsize) { 00248 errno = EINVAL; 00249 return -1; 00250 } 00251 /* For copy-on-write, we should trim on the diff file. Not yet 00252 * implemented. */ 00253 if(client->server->flags & F_COPYONWRITE) { 00254 DEBUG("TRIM not supported yet on copy-on-write exports"); 00255 return 0; 00256 } 00257 if (client->server->flags & F_TREEFILES) { 00258 /* start address of first block to be trimmed */ 00259 off_t min = ( ( req->from + TREEPAGESIZE - 1 ) / TREEPAGESIZE) * TREEPAGESIZE; 00260 /* start address of first block NOT to be trimmed */ 00261 off_t max = ( ( req->from + req->len ) / TREEPAGESIZE) * TREEPAGESIZE; 00262 while (min<max) { 00263 delete_treefile(client->exportname,client->exportsize,min); 00264 min+=TREEPAGESIZE; 00265 } 00266 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len); 00267 return 0; 00268 } 00269 FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0); 00270 FILE_INFO next; 00271 int i = 1; 00272 do { 00273 if(i<client->export->len) { 00274 next = g_array_index(client->export, FILE_INFO, i); 00275 } else { 00276 next.fhandle = -1; 00277 next.startoff = client->exportsize; 00278 } 00279 if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) { 00280 off_t reqoff = req->from - cur.startoff; 00281 off_t curlen = next.startoff - reqoff; 00282 off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff; 00283 punch_hole(cur.fhandle, reqoff, reqlen); 00284 } 00285 cur = next; 00286 i++; 00287 } while(i < client->export->len && cur.startoff < (req->from + req->len)); 00288 DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len); 00289 return 0; 00290 } 00291 00292 void myseek(int handle,off_t a) { 00293 if (lseek(handle, a, SEEK_SET) < 0) { 00294 err("Can not seek locally!\n"); 00295 } 00296 }
1.7.3