Network Block Device  @PACKAGE_VERSION@
nbdsrv.c
Go to the documentation of this file.
1 #include "config.h"
2 #include "nbd-debug.h"
3 
4 #include <nbdsrv.h>
5 
6 #include <assert.h>
7 #include <ctype.h>
8 #include <netdb.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <unistd.h>
14 
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <treefiles.h>
19 #include "backend.h"
20 #ifdef HAVE_SYS_MOUNT_H
21 #include <sys/mount.h>
22 #endif
23 
24 #define LINELEN 256 /**< Size of static buffer used to read the
25  authorization file (yuck) */
26 
27 #include <cliserv.h>
28 
29 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) {
30  struct addrinfo *res, *aitmp, hints;
31  char *masksep;
32  char privmask[strlen(mask)+1];
33  int masklen;
34  int addrlen = addr->sa_family == AF_INET ? 4 : 16;
35 
36  assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
37 
38  strcpy(privmask, mask);
39 
40  memset(&hints, 0, sizeof(hints));
41  hints.ai_family = AF_UNSPEC;
42  hints.ai_flags = AI_NUMERICHOST;
43 
44  if((masksep = strchr(privmask, '/'))) {
45  *masksep = '\0';
46  masklen = strtol(++masksep, NULL, 10);
47  } else {
48  masklen = addrlen * 8;
49  }
50 
51  int e;
52  if((e = getaddrinfo(privmask, NULL, &hints, &res))) {
53  g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e));
54  return false;
55  }
56  aitmp = res;
57  while(res) {
58  const uint8_t* byte_s;
59  uint8_t* byte_t;
60  uint8_t mask = 0;
61  int len_left = masklen;
62  if(res->ai_family != addr->sa_family) {
63  goto next;
64  }
65  switch(addr->sa_family) {
66  case AF_INET:
67  byte_s = (const uint8_t*)(&(((struct sockaddr_in*)addr)->sin_addr));
68  byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr));
69  break;
70  case AF_INET6:
71  byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr));
72  byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
73  break;
74  }
75  while(len_left >= 8) {
76  if(*byte_s != *byte_t) {
77  goto next;
78  }
79  byte_s++; byte_t++;
80  len_left -= 8;
81  }
82  if(len_left) {
83  mask = getmaskbyte(len_left);
84  if((*byte_s & mask) != (*byte_t & mask)) {
85  goto next;
86  }
87  }
88  freeaddrinfo(aitmp);
89  return true;
90  next:
91  res = res->ai_next;
92  }
93  freeaddrinfo(aitmp);
94  return false;
95 }
96 
97 uint8_t getmaskbyte(int masklen) {
98  if(masklen >= 8) {
99  return 0xFF;
100  }
101  uint8_t retval = 0;
102  for(int i = 7; i + masklen > 7; i--) {
103  retval |= 1 << i;
104  }
105 
106  return retval;
107 }
108 
109 int authorized_client(CLIENT *opts) {
110  FILE *f ;
111  char line[LINELEN];
112 
113  if (opts->server->authname == NULL) {
114  msg(LOG_INFO, "No authorization file, granting access.");
115  return 1;
116  }
117 
118  if ((f=fopen(opts->server->authname,"r"))==NULL) {
119  msg(LOG_INFO, "Can't open authorization file %s (%s).",
120  opts->server->authname, strerror(errno));
121  return 1 ;
122  }
123 
124  while (fgets(line,LINELEN,f)!=NULL) {
125  char* pos;
126  /* Drop comments */
127  if((pos = strchr(line, '#'))) {
128  *pos = '\0';
129  }
130  /* Skip whitespace */
131  pos = line;
132  while((*pos) && isspace(*pos)) {
133  pos++;
134  }
135  /* Skip content-free lines */
136  if(!(*pos)) {
137  continue;
138  }
139  if(address_matches(line, (struct sockaddr*)&opts->clientaddr, NULL)) {
140  fclose(f);
141  return 1;
142  }
143  }
144  fclose(f);
145  return 0;
146 }
147 
148 /**
149  * duplicate server
150  * @param s the old server we want to duplicate
151  * @return new duplicated server
152  **/
153 SERVER* dup_serve(const SERVER *const s) {
154  SERVER *serve = NULL;
155 
156  serve=g_new0(SERVER, 1);
157  if(serve == NULL)
158  return NULL;
159 
160  if(s->exportname)
161  serve->exportname = g_strdup(s->exportname);
162 
163  serve->expected_size = s->expected_size;
164 
165  if(s->listenaddr)
166  serve->listenaddr = g_strdup(s->listenaddr);
167 
168  if(s->authname)
169  serve->authname = g_strdup(s->authname);
170 
171  serve->flags = s->flags;
172  serve->virtstyle = s->virtstyle;
173  serve->cidrlen = s->cidrlen;
174 
175  if(s->prerun)
176  serve->prerun = g_strdup(s->prerun);
177 
178  if(s->postrun)
179  serve->postrun = g_strdup(s->postrun);
180 
181  if(s->transactionlog)
182  serve->transactionlog = g_strdup(s->transactionlog);
183 
184  if(s->servename)
185  serve->servename = g_strdup(s->servename);
186 
187  if(s->cowdir)
188  serve->cowdir = g_strdup(s->cowdir);
189 
190  serve->max_connections = s->max_connections;
191 
192  return serve;
193 }
194 
195 uint64_t size_autodetect(int fhandle) {
196  off_t es;
197  u64 bytes __attribute__((unused));
198  struct stat stat_buf;
199  int error;
200 
201 #ifdef HAVE_SYS_MOUNT_H
202 #ifdef HAVE_SYS_IOCTL_H
203 #ifdef BLKGETSIZE64
204  DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
205  if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
206  return bytes;
207  }
208 #endif /* BLKGETSIZE64 */
209 #endif /* HAVE_SYS_IOCTL_H */
210 #endif /* HAVE_SYS_MOUNT_H */
211 
212  DEBUG("looking for fhandle size with fstat\n");
213  stat_buf.st_size = 0;
214  error = fstat(fhandle, &stat_buf);
215  if (!error) {
216  /* always believe stat if a regular file as it might really
217  * be zero length */
218  if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
219  return (uint64_t)stat_buf.st_size;
220  } else {
221  DEBUG("fstat failed: %s", strerror(errno));
222  }
223 
224  DEBUG("looking for fhandle size with lseek SEEK_END\n");
225  es = lseek(fhandle, (off_t)0, SEEK_END);
226  if (es > ((off_t)0)) {
227  return (uint64_t)es;
228  } else {
229  DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
230  }
231 
232  DEBUG("Could not find size of exported block device: %s", strerror(errno));
233  return UINT64_MAX;
234 }
235 
236 int exptrim(struct nbd_request* req, CLIENT* client) {
237  /* Don't trim when we're read only */
238  if(client->server->flags & F_READONLY) {
239  errno = EINVAL;
240  return -1;
241  }
242  /* Don't trim beyond the size of the device, please */
243  if(req->from + req->len > client->exportsize) {
244  errno = EINVAL;
245  return -1;
246  }
247  /* For copy-on-write, we should trim on the diff file. Not yet
248  * implemented. */
249  if(client->server->flags & F_COPYONWRITE) {
250  DEBUG("TRIM not supported yet on copy-on-write exports");
251  return 0;
252  }
253  if (client->server->flags & F_TREEFILES) {
254  /* start address of first block to be trimmed */
255  off_t min = ( ( req->from + TREEPAGESIZE - 1 ) / TREEPAGESIZE) * TREEPAGESIZE;
256  /* start address of first block NOT to be trimmed */
257  off_t max = ( ( req->from + req->len ) / TREEPAGESIZE) * TREEPAGESIZE;
258  while (min<max) {
259  delete_treefile(client->exportname,client->exportsize,min);
260  min+=TREEPAGESIZE;
261  }
262  DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
263  return 0;
264  }
265  FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
266  FILE_INFO next;
267  int i = 1;
268  do {
269  if(i<client->export->len) {
270  next = g_array_index(client->export, FILE_INFO, i);
271  } else {
272  next.fhandle = -1;
273  next.startoff = client->exportsize;
274  }
275  if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) {
276  off_t reqoff = req->from - cur.startoff;
277  off_t curlen = next.startoff - reqoff;
278  off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff;
279  punch_hole(cur.fhandle, reqoff, reqlen);
280  }
281  cur = next;
282  i++;
283  } while(i < client->export->len && cur.startoff < (req->from + req->len));
284  DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
285  return 0;
286 }
287 
288 void myseek(int handle,off_t a) {
289  if (lseek(handle, a, SEEK_SET) < 0) {
290  err("Can not seek locally!\n");
291  }
292 }
gchar * servename
name of the export as selected by nbd-client
Definition: nbdsrv.h:44
GArray * export
array of FILE_INFO of exported files; array size is always 1 unless we're doing the multiple file opt...
Definition: nbdsrv.h:58
void delete_treefile(char *name, off_t size, off_t pos)
Definition: treefiles.c:37
Variables associated with a server.
Definition: nbdsrv.h:30
uint8_t getmaskbyte(int masklen)
Gets a byte to allow for address masking.
Definition: nbdsrv.c:98
void err(const char *s)
Definition: cliserv.c:59
SERVER * server
The server this client is getting data from.
Definition: nbdsrv.h:62
gchar * postrun
command that will be ran after the client disconnects
Definition: nbdsrv.h:42
int exptrim(struct nbd_request *req, CLIENT *client)
Punch a hole in the backend file (if supported by the current system).
Definition: nbdsrv.c:237
#define msg(prio,...)
Logging macros.
Definition: nbdsrv.h:124
int fhandle
file descriptor
Definition: nbdsrv.h:79
bool address_matches(const char *mask, const struct sockaddr *addr, GError **err)
Check whether a given address matches a given netmask.
Definition: nbdsrv.c:30
#define F_COPYONWRITE
flag to tell us a file is exported using copyonwrite
Definition: nbdsrv.h:131
off_t startoff
starting offset of this file
Definition: nbdsrv.h:80
uint64_t from
Definition: nbd.h:69
gchar * cowdir
directory for copy-on-write diff files.
Definition: nbdsrv.h:47
#define F_READONLY
Per-export flags:
Definition: nbdsrv.h:129
struct sockaddr_storage clientaddr
peer, in binary format, network byte order
Definition: nbdsrv.h:56
int flags
flags associated with this exported file
Definition: nbdsrv.h:36
#define TREEPAGESIZE
tree (block) files uses those chunks
Definition: treefiles.h:5
#define F_TREEFILES
flag to tell us a file is exported using -t
Definition: nbdsrv.h:144
gchar * exportname
(unprocessed) filename of the file we're exporting
Definition: nbdsrv.h:31
gchar * transactionlog
filename for transaction log
Definition: nbdsrv.h:46
gchar * listenaddr
The IP address we're listening on.
Definition: nbdsrv.h:34
Variables associated with a client connection.
Definition: nbdsrv.h:53
struct nbd_reply __attribute__
Failed to get address info.
Definition: nbdsrv.h:108
SERVER * dup_serve(const SERVER *const s)
duplicate server
Definition: nbdsrv.c:154
Variables associated with an open file.
Definition: nbdsrv.h:78
void myseek(int handle, off_t a)
seek to a position in a file, with error handling.
Definition: nbdsrv.c:289
uint64_t size_autodetect(int fhandle)
Detect the size of a file.
Definition: nbdsrv.c:196
int max_connections
maximum number of opened connections
Definition: nbdsrv.h:45
VIRT_STYLE virtstyle
The style of virtualization, if any.
Definition: nbdsrv.h:37
uint64_t expected_size
size of the exported file as it was told to us through configuration
Definition: nbdsrv.h:32
gchar * prerun
command to be ran after connecting a client, but before starting to serve
Definition: nbdsrv.h:40
uint64_t exportsize
size of the file we're exporting
Definition: nbdsrv.h:54
#define DEBUG(...)
Definition: nbd-debug.h:8
char * authname
filename of the authorization file
Definition: nbdsrv.h:35
#define LINELEN
Size of static buffer used to read the authorization file (yuck)
Definition: nbdsrv.c:24
void punch_hole(int fd, off_t off, off_t len)
Definition: nbd-server.c:1216
uint32_t len
Definition: nbd.h:70
#define NBDS_ERR
Error domain common for all NBD server errors.
Definition: nbdsrv.h:88
char handle[8]
Definition: nbd.h:39
uint8_t cidrlen
The length of the mask when we use CIDR-style virtualization.
Definition: nbdsrv.h:38
char * exportname
(processed) filename of the file we're exporting
Definition: nbdsrv.h:57
int authorized_client(CLIENT *opts)
Check whether a client is allowed to connect.
Definition: nbdsrv.c:110