17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <treefiles.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 #include <sys/ioctl.h>
24 #ifdef HAVE_SYS_MOUNT_H
25 #include <sys/mount.h>
28 #define LINELEN 256 /**< Size of static buffer used to read the
29 authorization file (yuck) */
32 bool address_matches(const char* mask
, const struct sockaddr
* addr
, GError
** err
) {
33 struct addrinfo
*res
, *aitmp
, hints
;
35 char privmask
[strlen(mask
)+1];
37 int addrlen
= addr
->sa_family
== AF_INET
? 4 : 16;
38 #define IPV4_MAP_PREFIX 12
39 uint8_t ipv4_mapped
[IPV4_MAP_PREFIX
+4] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40 255, 255, 0, 0, 0, 0};
42 strcpy(privmask
, mask
);
44 memset(&hints
, 0, sizeof(hints
));
45 hints
.ai_family
= AF_UNSPEC
;
46 hints
.ai_flags
= AI_NUMERICHOST
;
48 if((masksep
= strchr(privmask
, '/'))) {
50 masklen
= strtol(++masksep
, NULL
, 10);
52 masklen
= addrlen
* 8;
55 if (masklen
> addrlen
* 8 || masklen
< 0) {
56 g_set_error(err
, NBDS_ERR
, NBDS_ERR_CFILE_VALUE_INVALID
, "could not parse netmask line: invalid mask length");
61 if((e
= getaddrinfo(privmask
, NULL
, &hints
, &res
))) {
62 g_set_error(err
, NBDS_ERR
, NBDS_ERR_GAI
, "could not parse netmask line: %s", gai_strerror(e
));
67 assert(addr
->sa_family
== AF_INET
|| addr
->sa_family
== AF_INET6
);
68 const uint8_t* byte_s
;
71 int len_left
= masklen
;
72 if(res
->ai_family
== addr
->sa_family
) {
73 /* Both addresses are the same address family so do a simple comparison */
74 switch(addr
->sa_family
) {
76 byte_s
= (const uint8_t*)(&(((struct sockaddr_in
*)addr
)->sin_addr
));
77 byte_t
= (uint8_t*)(&(((struct sockaddr_in
*)(res
->ai_addr
))->sin_addr
));
80 byte_s
= (const uint8_t*)(&(((struct sockaddr_in6
*)addr
)->sin6_addr
));
81 byte_t
= (uint8_t*)(&(((struct sockaddr_in6
*)(res
->ai_addr
))->sin6_addr
));
85 /* Addresses are different families, compare IPv4-mapped IPv6 address */
86 switch(addr
->sa_family
) {
88 /* Map client address to IPv6 for comparison */
89 memcpy(&ipv4_mapped
[IPV4_MAP_PREFIX
], &(((struct sockaddr_in
*)addr
)->sin_addr
), 4);
90 byte_s
= (const uint8_t*)&ipv4_mapped
;
91 byte_t
= (uint8_t*)(&(((struct sockaddr_in6
*)(res
->ai_addr
))->sin6_addr
));
92 len_left
+= IPV4_MAP_PREFIX
* 8;
95 byte_s
= (const uint8_t*)(&(((struct sockaddr_in6
*)addr
)->sin6_addr
));
96 /* Map res to IPv6 for comparison */
97 memcpy(&ipv4_mapped
[IPV4_MAP_PREFIX
], &(((struct sockaddr_in
*)(res
->ai_addr
))->sin_addr
), 4);
98 byte_t
= &ipv4_mapped
[0];
99 len_left
+= IPV4_MAP_PREFIX
* 8;
103 while(len_left
>= 8) {
104 if(*byte_s
!= *byte_t
) {
111 mask
= getmaskbyte(len_left
);
112 if((*byte_s
& mask
) != (*byte_t
& mask
)) {
125 uint8_t getmaskbyte(int masklen
) {
130 for(int i
= 7; i
+ masklen
> 7; i
--) {
137 int authorized_client(CLIENT
*opts
) {
141 if (opts
->server
->authname
== NULL
) {
142 msg(LOG_INFO
, "No authorization file, granting access.");
146 if ((f
=fopen(opts
->server
->authname
,"r"))==NULL
) {
147 msg(LOG_INFO
, "Can't open authorization file %s (%s).",
148 opts
->server
->authname
, strerror(errno
));
152 while (fgets(line
,LINELEN
,f
)!=NULL
) {
156 if((pos
= strchr(line
, '#'))) {
159 /* Skip whitespace */
161 while((*pos
) && isspace(*pos
)) {
164 /* Skip content-free lines */
168 /* Trim trailing whitespace */
170 while ((*endpos
) && !isspace(*endpos
))
173 if(address_matches(pos
, (struct sockaddr
*)&opts
->clientaddr
, NULL
)) {
184 * @param s the old server we want to duplicate
185 * @return new duplicated server
187 SERVER
* dup_serve(const SERVER
*const s
) {
188 SERVER
*serve
= NULL
;
190 serve
=g_new0(SERVER
, 1);
195 serve
->exportname
= g_strdup(s
->exportname
);
197 serve
->expected_size
= s
->expected_size
;
200 serve
->listenaddr
= g_strdup(s
->listenaddr
);
203 serve
->authname
= g_strdup(s
->authname
);
205 serve
->flags
= s
->flags
;
206 serve
->virtstyle
= s
->virtstyle
;
207 serve
->cidrlen
= s
->cidrlen
;
210 serve
->prerun
= g_strdup(s
->prerun
);
213 serve
->postrun
= g_strdup(s
->postrun
);
215 if(s
->transactionlog
)
216 serve
->transactionlog
= g_strdup(s
->transactionlog
);
219 serve
->servename
= g_strdup(s
->servename
);
222 serve
->cowdir
= g_strdup(s
->cowdir
);
224 serve
->max_connections
= s
->max_connections
;
229 uint64_t size_autodetect(int fhandle
) {
231 u64 bytes
__attribute__((unused
));
232 struct stat stat_buf
;
235 #ifdef HAVE_SYS_MOUNT_H
236 #ifdef HAVE_SYS_IOCTL_H
238 DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
239 if (!ioctl(fhandle
, BLKGETSIZE64
, &bytes
) && bytes
) {
242 #endif /* BLKGETSIZE64 */
243 #endif /* HAVE_SYS_IOCTL_H */
244 #endif /* HAVE_SYS_MOUNT_H */
246 DEBUG("looking for fhandle size with fstat\n");
247 stat_buf
.st_size
= 0;
248 error
= fstat(fhandle
, &stat_buf
);
250 /* always believe stat if a regular file as it might really
252 if (S_ISREG(stat_buf
.st_mode
) || (stat_buf
.st_size
> 0))
253 return (uint64_t)stat_buf
.st_size
;
255 DEBUG("fstat failed: %s", strerror(errno
));
258 DEBUG("looking for fhandle size with lseek SEEK_END\n");
259 es
= lseek(fhandle
, (off_t
)0, SEEK_END
);
260 if (es
> ((off_t
)0)) {
263 DEBUG("lseek failed: %d", errno
==EBADF
?1:(errno
==ESPIPE
?2:(errno
==EINVAL
?3:4)));
266 DEBUG("Could not find size of exported block device: %s", strerror(errno
));
270 int exptrim(struct nbd_request
* req
, CLIENT
* client
) {
271 /* Caller did range checking */
272 assert(!(client
->server
->flags
& F_READONLY
));
273 assert(req
->from
+ req
->len
<= client
->exportsize
);
274 /* For copy-on-write, we should trim on the diff file. Not yet
276 if(client
->server
->flags
& F_COPYONWRITE
) {
277 DEBUG("TRIM not supported yet on copy-on-write exports");
280 if (client
->server
->flags
& F_TREEFILES
) {
281 /* start address of first block to be trimmed */
282 off_t min
= ( ( req
->from
+ TREEPAGESIZE
- 1 ) / TREEPAGESIZE
) * TREEPAGESIZE
;
283 /* start address of first block NOT to be trimmed */
284 off_t max
= ( ( req
->from
+ req
->len
) / TREEPAGESIZE
) * TREEPAGESIZE
;
286 delete_treefile(client
->exportname
,client
->exportsize
,min
);
289 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req
->from
, (unsigned long long) req
->len
);
292 FILE_INFO cur
= g_array_index(client
->export
, FILE_INFO
, 0);
296 if(i
<client
->export
->len
) {
297 next
= g_array_index(client
->export
, FILE_INFO
, i
);
300 next
.startoff
= client
->exportsize
;
302 if(cur
.startoff
<= req
->from
&& next
.startoff
- cur
.startoff
>= req
->len
) {
303 off_t reqoff
= req
->from
- cur
.startoff
;
304 off_t curlen
= next
.startoff
- reqoff
;
305 off_t reqlen
= curlen
- reqoff
> req
->len
? req
->len
: curlen
- reqoff
;
306 punch_hole(cur
.fhandle
, reqoff
, reqlen
);
310 } while(i
< client
->export
->len
&& cur
.startoff
< (req
->from
+ req
->len
));
311 DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req
->from
, (unsigned long long) req
->len
);
315 pthread_mutex_t cntmutex
= PTHREAD_MUTEX_INITIALIZER
;
317 SERVER
* serve_inc_ref(SERVER
*s
) {
318 pthread_mutex_lock(&cntmutex
);
320 pthread_mutex_unlock(&cntmutex
);
324 SERVER
* serve_dec_ref(SERVER
*s
) {
325 pthread_mutex_lock(&cntmutex
);
326 if(--(s
->refcnt
) == 0) {
330 pthread_mutex_unlock(&cntmutex
);
334 void serve_clear_element(SERVER
**server
) {
335 serve_dec_ref(*server
);