117 Commits

Author SHA1 Message Date
Chris Elsworth
2663173d1a Merge branch 'release-to-master' into 'master'
Release 0.5.0 to master

See merge request open-source/flexnbd-c!60
2019-01-11 13:34:34 +00:00
James F. Carter
3448ff15b8 release 0.5.0 2019-01-11 10:37:48 +00:00
James Carter
aff33bce10 Merge branch '37-unexplained-mirror-timeout-causing-migrations-to-stall' into 'develop'
Resolve "Unexplained mirror timeout causing migrations to stall"

Closes #37

See merge request open-source/flexnbd-c!59
2018-12-10 12:36:43 +00:00
Patrick J Cherry
6b1a877dc3 Tweaked test file size, and removed debug ENV fiddling 2018-12-07 22:43:50 +00:00
Patrick J Cherry
e5133a50bd Slow down start/stop. Enable DEBUG
Trying to working why this is failing in gitlab-ci
2018-12-07 22:09:21 +00:00
Patrick J Cherry
39400f2e09 Fixed issue number. 2018-12-07 21:50:56 +00:00
Patrick J Cherry
52690f5382 Updated changelog 2018-12-07 21:48:55 +00:00
Patrick J Cherry
a4d641b215 Ensure ev abandon_watcher is stopped before reuse. 2018-12-07 21:47:14 +00:00
James Carter
416d8bde96 Merge branch '40-when-migrating-qemu-discs-from-one-tail-to-another-there-s-an-edge-case-where-flexnbd-freezes-and-the-vm-eventually-disconnects-and-becomes-unstartable' into 'develop'
Resolve "When migrating QEmu discs from one tail to another there's an edge case where flexnbd freezes and the VM eventually disconnects and becomes unstartable."

Closes #40

See merge request open-source/flexnbd-c!58
2018-12-07 17:12:21 +00:00
Patrick J Cherry
654d277453 Updated changelog 2018-12-07 16:40:53 +00:00
Patrick J Cherry
842e7d362d Ensure control socket is closed first, and wait for it to close. 2018-12-07 16:32:58 +00:00
Patrick J Cherry
5839a36ab1 Remove useless function definition 2018-12-07 15:05:19 +00:00
Patrick J Cherry
70a3a4bb55 Close the control socket during cleanup
This should prevent further requests coming in, triggering deadlocks.
2018-12-07 15:02:55 +00:00
Patrick J Cherry
ce9499efce Rubocop; add test to bombard a migration source with status commands 2018-12-07 13:59:49 +00:00
Chris Elsworth
edb42700d0 Merge branch 'release-to-master' into 'master'
Release 0.4.0

See merge request open-source/flexnbd-c!57
2018-11-15 15:05:23 +00:00
Chris Elsworth
e5f7038127 Merge branch 'release-to-master' into 'develop'
Updated changelog for release

See merge request open-source/flexnbd-c!56
2018-11-15 14:28:43 +00:00
Patrick J Cherry
8bc6ebbb0f Updated changelog for release 2018-11-15 14:24:59 +00:00
Chris Elsworth
eb45b5e483 Merge branch '39-following-a-proxy-timeout-a-write-request-is-not-restarted-or-abandoned-leading-to-bad-magic-errors-following-reconnection' into 'develop'
Resolve "Following a proxy timeout, a write request is not restarted (or abandoned) leading to "bad magic" errors following reconnection"

Closes #39

See merge request open-source/flexnbd-c!54
2018-11-15 11:29:24 +00:00
Patrick J Cherry
256cba79e3 Added note about the new environment variable 2018-11-14 17:31:42 +00:00
Patrick J Cherry
5d1b0472de Fixed typo 2018-11-14 17:21:08 +00:00
Patrick J Cherry
93308bbda1 Added jessie back. 2018-11-14 17:20:33 +00:00
Patrick J Cherry
bb5271cea3 Remove jessie packaging :'( 2018-11-14 16:58:33 +00:00
Patrick J Cherry
b7b50faa17 Updated comments 2018-11-14 16:58:05 +00:00
Patrick J Cherry
b26b308e68 Add test to check when proxy times-out mid-write to upstream
I've add to add code to allow the environment to specify the upstream
tiemout so we don't have to wait 30s for this test to happen.
2018-11-14 16:49:55 +00:00
Patrick J Cherry
3e00a88d45 Removed debug 2018-11-14 10:23:58 +00:00
Patrick J Cherry
3fe9f2c6a1 Removed a couple of gotos.
Hadn't you heard?  They're considered harmful.
2018-11-14 10:23:05 +00:00
Patrick J Cherry
391a17bfcc Updated changelog 2018-11-13 21:38:36 +00:00
Patrick J Cherry
9b1518806d Move state-resetting to after before the init is read from upstream
This removes repetition and ensures a constant state before the upstream
init is read.
2018-11-13 21:33:06 +00:00
Patrick J Cherry
1225a28d41 Reset proxy req size/needle on timeout 2018-11-13 16:42:13 +00:00
Patrick J Cherry
c9d30a9bde Merge branch 'release-to-master' into 'master'
Release 0.3.0

See merge request open-source/flexnbd-c!53
2018-04-24 13:12:53 +01:00
James Carter
b18c46606f Merge branch 'release-to-master' into 'develop'
Updated changelog for release 0.3.0

See merge request open-source/flexnbd-c!52
2018-04-24 12:12:06 +01:00
Patrick J Cherry
b3cea813e4 Updated changelog for release 2018-04-24 12:06:06 +01:00
James Carter
3e0d30f6b9 Merge branch 'reinstate-sync-after-every-write' into 'develop'
Reinstate sync after every write

See merge request open-source/flexnbd-c!51
2018-04-24 12:02:53 +01:00
Patrick J Cherry
3b1a150315 Updated changelgo 2018-04-24 10:27:46 +01:00
Patrick J Cherry
ead6328d80 Force sync after every write 2018-04-24 10:27:02 +01:00
James Carter
20b4f069c8 Merge branch 'release-to-master' into 'develop'
Merge back to develop

See merge request open-source/flexnbd-c!49
2018-02-20 11:52:37 +00:00
James Carter
a4f1956a56 Merge branch 'release-to-master' into 'master'
Release to master

See merge request open-source/flexnbd-c!50
2018-02-20 11:52:07 +00:00
Patrick J Cherry
331ca4be14 Updated changelog for release 2018-02-20 11:45:42 +00:00
James Carter
fb5714765c Merge branch 'fix-formatting' into 'develop'
Formatted all code using `indent`

See merge request open-source/flexnbd-c!47
2018-02-20 11:42:25 +00:00
Patrick J Cherry
af3bb16ff7 Merge branch 'develop' into fix-formatting 2018-02-20 11:06:58 +00:00
Patrick J Cherry
9cbcc7c95a Added note about the test file formatting 2018-02-20 11:05:36 +00:00
Patrick J Cherry
8893cd06c4 Re-formatted tests with a bit of tinkering by hand 2018-02-20 11:02:33 +00:00
James Carter
166db9b1f7 Merge branch 'enable-flags-test' into 'develop'
Enable request flags test

See merge request open-source/flexnbd-c!48
2018-02-20 10:23:42 +00:00
Patrick J Cherry
103bd7ad5b Undo formatting on test suite -- it wasn't right 2018-02-20 10:13:42 +00:00
Patrick J Cherry
7bee1aadfe Enable request flags test
Missed this out when I wrote the test!
2018-02-20 10:11:38 +00:00
Patrick J Cherry
f47f56d4c4 Formatted all code using indent 2018-02-20 10:05:35 +00:00
James Carter
19a1127bde Merge branch 'fix-correct-num-clients-status' into 'develop'
Call the thread cleanup code when requesting `status`

See merge request open-source/flexnbd-c!46
2018-02-20 09:51:37 +00:00
James Carter
073a4ac0fa Merge branch '35-incorrect-struct-type-used-in-readwrite-c' into 'develop'
Resolve "Incorrect struct type used in readwrite.c"

Closes #35

See merge request open-source/flexnbd-c!41
2018-02-20 09:50:25 +00:00
Patrick J Cherry
623007bfff Remove last reference to removed test_gets_num_clients 2018-02-19 10:22:01 +00:00
Patrick J Cherry
27a94a807e Remove the test_gets_num_clients test from the C unit tests
This test was causing problems by using dummy pointers to simulate
connections.  When calling the cleanup code, these pointers were
thought to be real, and the code attemtped to clean up threads
referenced by those pointers, causing a segfault.

I've reimplemented the test in the ruby acceptance suite.
2018-02-16 13:46:31 +00:00
Patrick J Cherry
1407407ff4 Updated changelog 2018-02-16 13:00:31 +00:00
Patrick J Cherry
d0439dab88 Call the thread cleanup code when requesting status
This ensures the correct number of connected clients is returned when
the status command is issued.

Previously the thread pool would only be cleaned up on a new connection.
2018-02-16 12:58:03 +00:00
James F. Carter
9f56f38f42 Merge branch 'rationalise-ld-preload-tests' into develop 2018-02-14 16:48:57 +00:00
Chris Elsworth
370d04d971 Merge branch 'take-request-response-size-into-malloc' into 'develop'
Update proxy malloc to add the struct size onto the request/response buffer

See merge request open-source/flexnbd-c!45
2018-02-14 05:28:24 +00:00
Patrick J Cherry
099e29de91 Merge branch 'develop' into 'take-request-response-size-into-malloc'
# Conflicts:
#   debian/changelog
2018-02-13 17:06:41 +00:00
Patrick J Cherry
2e17e8955f Added tests for NBD_MAX_SIZE
This constant is only used in the proxy, so the tests only cover proxy
mode.
2018-02-13 17:04:51 +00:00
Patrick J Cherry
bb1f6ecdf5 Updated changelog 2018-02-13 15:51:09 +00:00
Patrick J Cherry
158379ba7a Use correct constant name. 2018-02-12 19:11:24 +00:00
Patrick J Cherry
1c66b56af1 Update proxy malloc to add the struct size onto the request/response buffer
This alters the meaning of NBD_MAX_SIZE to be the actual max request size
we'll accept over nbd.  Previously it was *nearly* the max size we'd
accept depending on the size of the struct.
2018-02-12 19:04:29 +00:00
Ian Chilton
03d9eb01b5 Merge branch 'increase-log-level-for-readloop-failures' into 'develop'
Increase log level for readloop failures, which might help with diagnosis

See merge request open-source/flexnbd-c!44
2018-02-09 15:38:48 +00:00
Patrick J Cherry
cdcd527544 Refactored read_reply to compare the network-byte-ordered handle 2018-02-09 12:18:34 +00:00
Patrick J Cherry
169d40f575 Increase log level for readloop failures, which might help with diagnosis 2018-02-09 11:57:07 +00:00
Patrick J Cherry
21f384e343 Updated changelog 2018-02-09 11:44:28 +00:00
Patrick J Cherry
9817fd7b0a Final tidies, comments etc. 2018-02-09 11:42:25 +00:00
Patrick J Cherry
195de41d86 Remove extra line 2018-02-09 11:32:26 +00:00
Patrick J Cherry
5b350e10e5 Merge branch 'develop' into '35-incorrect-struct-type-used-in-readwrite-c'
# Conflicts:
#   debian/changelog
2018-02-09 11:29:48 +00:00
Patrick J Cherry
b75a6529d0 Move LdPreload include to correct place 2018-02-09 10:41:24 +00:00
Patrick J Cherry
8e67180999 Check that TCP_NODELAY is set on upstream sockets on reconnection
Also rationalize the test to see if a function has been called.  Still
not great, but getting there :)
2018-02-09 10:26:08 +00:00
Patrick J Cherry
c053a54faa Added test to cover setsockopt for tcpkeepalive 2018-02-08 23:07:17 +00:00
Patrick J Cherry
ebacf738bc Tidy up ld preload hacks 2018-02-08 22:28:34 +00:00
James Carter
c4bab3f81f Merge branch 'truncate-odd-sized-discs' into 'develop'
Discs must be sized in multiples of 512 bytes or odd things happen

See merge request open-source/flexnbd-c!42
2018-02-08 16:49:36 +00:00
Patrick J Cherry
a19267b377 Adjust block-rounding line to match in serve.c 2018-02-08 16:37:36 +00:00
Patrick J Cherry
23d9ff587e Updated changelog 2018-02-08 16:36:20 +00:00
Patrick J Cherry
347b7978e4 Discs must be sized in multiples of 512 bytes or odd things happen
In #36 some of the odd errors were due to seeks beyond the end of the
disc.  This was because the disc was "specially crafted" to be 25GB + 1
byte, which doesn't fit into the normal 512 byte sectors expected of a
disc.  This lead to reads going beyond the end of the disc etc.

If a similarly evil disc is used with `losetup`, it just ignores the
last bytes of the disc that don't fit into 512 chunks.  This is what
that patch does, logging an error at the same time.
2018-02-08 16:31:28 +00:00
Patrick J Cherry
f8fec5f57e Alter struct types to reflect reality, avoiding mixing "host" and "raw" structs 2018-02-08 15:46:34 +00:00
James Carter
1672b4b88b Merge branch '36-breaks-when-trying-to-install-debian-from-cd' into 'develop'
Resolve "breaks when trying to install debian from CD"

Closes #36

See merge request open-source/flexnbd-c!40
2018-02-08 13:59:12 +00:00
Patrick J Cherry
5e9dbbd626 Updated changelgo 2018-02-08 13:32:10 +00:00
Patrick J Cherry
8beb3f0af6 Allow proxy to pass NBD protocol errors downstream; server returns EINVAL/ENOSPC appropriately
Previously the proxy would just disconnect when it saw an NBD protocol
error, and retry the operation it was in the middle of.

Additionally, the server needs to return the correct error types when
this happens.
2018-02-08 13:19:51 +00:00
James Carter
806de13024 Merge branch 'try-flags' into 'develop'
Set flags to show we can accept FUA and FLUSH commands

See merge request open-source/flexnbd-c!38
2018-02-08 11:18:31 +00:00
Patrick J Cherry
f71b872622 Only set up LD_PRELOAD for tests that actually need it. 2018-02-07 22:05:07 +00:00
Patrick J Cherry
79181b3153 Added LD_PRELOAD library to monitor msync calls in testing 2018-02-07 21:45:20 +00:00
Patrick J Cherry
55548cc969 Change ordering of @env configuration/start so we can alter the blocksize.
argh.
2018-02-06 10:24:54 +00:00
Patrick J Cherry
9bf3b52d54 Call proxy_finish_connect_to_upstream when reconnecting, setting
TCP_NODELAY
2018-02-06 10:02:16 +00:00
Patrick J Cherry
da35187af0 Allow blocksize to be changed in Environment
This number is peppered all over the test suite, so changing @blocksize
for everything is not a goer, when we really only need to change it for
one test.
2018-02-06 09:55:32 +00:00
Patrick J Cherry
7704f9e5c8 Fix tests to reflect new filesize. 2018-02-06 07:57:40 +00:00
Patrick J Cherry
3a86870c9f Use sysconf to determine actual page size for msync
Also added comments in tests around testing for msync offsets/lengths.
2018-02-06 07:32:58 +00:00
Patrick J Cherry
6d6948af09 Fix offset calculation for partial msyncs to go to nearest 4k block
Previously they were always set to zero.
2018-02-05 23:05:00 +00:00
Patrick J Cherry
c423900f02 Fix typo 2018-02-05 17:04:23 +00:00
Patrick J Cherry
afa1bb0efb Use msync rather than fsync to flush the entire disc
This involves storing the size of the mapped disc in the client struct,
and then supplying that to the msync command.
2018-02-05 17:01:32 +00:00
Patrick J Cherry
ad2014ac9d Fixed long-standing bug with h2r functions being back to front
h2r seemd to be using beXXtoh functions instead of htobeXX.  Foruntately
ROT13 works symmetrically on our systems..!
2018-02-05 16:16:17 +00:00
Patrick J Cherry
d1dc7392c2 Open file with O_NOATIME, not O_SYNC
O_SYNC is not necessary as we're not doing direct writes to the file.
O_NOATIME might give some speed boost.
2018-02-05 16:15:36 +00:00
Patrick J Cherry
ba59a4c03f Updated changelog 2018-02-05 08:15:56 +00:00
Patrick J Cherry
2b58468800 Added test for FUA acceptance.
Although I think this might be a bit useless as servers normally just
ingore flags.
2018-02-03 20:29:15 +00:00
Patrick J Cherry
4d9db4d6e9 Added basic FLUSH test 2018-02-03 20:10:47 +00:00
Patrick J Cherry
d6057a4244 Use 'English' in ruby 2018-02-02 21:41:07 +00:00
Patrick J Cherry
1d98ba1d3e Further rubocopping 2018-02-02 21:36:30 +00:00
Patrick J Cherry
9c48da82cc Rubocop 2018-02-02 21:34:14 +00:00
Patrick J Cherry
1b7b688f7a Tidied up nbd init test 2018-02-02 21:30:55 +00:00
Patrick J Cherry
3410ccd4c5 Fixed up commenting around our advertised flags. 2018-02-02 20:50:48 +00:00
Patrick J Cherry
051576df6d Remove warnings about Object#timeout 2018-02-02 20:46:46 +00:00
Patrick J Cherry
9eb7072f49 Removed some extra spaces I'd added 2018-02-02 20:46:25 +00:00
Patrick J Cherry
6aa5907f5e Tidied constants up a bit 2018-02-02 20:34:49 +00:00
Patrick J Cherry
72c8c6f757 Altered test to check for type as a 16-bit uint; added flags test 2018-02-02 20:30:39 +00:00
Patrick J Cherry
b22b99d9b9 Fix fill_request to set flags as well as type. 2018-02-02 20:28:00 +00:00
Patrick J Cherry
ad001cb83c Tidy comments 2018-02-02 16:17:01 +00:00
Patrick J Cherry
f37e4438c8 Merge branch 'develop' into try-flags 2018-02-02 16:05:57 +00:00
Chris Elsworth
084d429961 Merge branch 'update-changelog-for-mr35' into 'develop'
Updated changelog for !35

See merge request open-source/flexnbd-c!39
2018-02-02 14:57:58 +00:00
Patrick J Cherry
1883bee43c Updated changelog for !35 2018-02-02 14:52:26 +00:00
Patrick J Cherry
68a196e93d Allow the proxy connection to pass through flags from upstream. 2018-02-02 10:30:40 +00:00
Patrick J Cherry
1f0ef0aad6 Implement FLUSH command and honour FUA flag
I changed the request struct to break the 32 bits reserved for the
request type into two.  The first part of this is used for the flags
(such as FUA), and the second part for the command type.  Previously
we'd masked the top two bytes, thus ignoring any flags.
2018-02-01 22:13:59 +00:00
Patrick J Cherry
25cc084108 First steps towards implementing flags as part of oldstyle negotiation 2018-02-01 19:25:36 +00:00
Patrick J Cherry
f2fa00260b Merge branch 'avoid-crash-on-timeout' into 'develop'
avoid fatal error on client connection timeout

See merge request open-source/flexnbd-c!36
2018-01-26 16:04:51 +00:00
James F. Carter
b2007c9dad debian: uodate changelog 2018-01-26 15:06:26 +00:00
James F. Carter
9b1781164a avoid fatal error on client connection timeout 2018-01-26 15:03:44 +00:00
Ian Chilton
1f99929589 Merge branch 'develop' into 'develop'
Develop

See merge request open-source/flexnbd-c!35
2018-01-24 12:42:49 +00:00
Patrick J Cherry
072f4be3c0 Merge branch 'release' into 'master'
Release

See merge request !31
2017-07-14 17:42:33 +01:00
James Carter
b4426f5dce Merge branch 'develop' into 'master'
Merge develop into master for release.

See merge request !29
2017-03-23 13:21:40 +00:00
108 changed files with 8916 additions and 8481 deletions

View File

@@ -2,17 +2,20 @@ stages:
- package - package
- publish - publish
package:jessie: &package .package: &package
stage: package stage: package
image: $CI_REGISTRY/docker-images/layers:$DISTRO-deb image: $CI_REGISTRY/docker-images/layers:$DISTRO-deb
variables:
DISTRO: jessie
script: script:
- package - package
artifacts: artifacts:
paths: paths:
- pkg/ - pkg/
package:jessie:
<<: *package
variables:
DISTRO: jessie
package:stretch: package:stretch:
<<: *package <<: *package
variables: variables:

24
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,24 @@
# Contribution guide
The code is formatted using the K&R style of "indent".
```
indent -kr <files go here>
```
The C unit tests have also been indented in the same way, but manually adjsted
such that the functions follow the normal libcheck layout.
```c
START_TEST( ... ) {
}
END TEST
```
Indent tends to mangle the `END_TEST` macro, so that will need adjusting if
`indent` is run over the test files again.

View File

@@ -109,7 +109,6 @@ install:
clean: clean:
rm -rf build/* rm -rf build/*
.PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance .PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance
# Include extra dependencies at the end, NOT before 'all' # Include extra dependencies at the end, NOT before 'all'

View File

@@ -169,6 +169,11 @@ That is, the '=' is required. This is a limitation of getopt-long.
If no cache size is given, a size of 4096 bytes is assumed. Caching can If no cache size is given, a size of 4096 bytes is assumed. Caching can
be explicitly disabled by setting a size of 0. be explicitly disabled by setting a size of 0.
ENVIRONMENT
FLEXNBD_UPSTREAM_TIMEOUT The timeout in seconds for the proxy communicating
with the upstream server defaults to 30 seconds.
BUGS BUGS
Should be reported via GitHub. Should be reported via GitHub.

50
debian/changelog vendored
View File

@@ -1,9 +1,53 @@
flexnbd (0.1.8) UNRELEASED; urgency=medium flexnbd (0.5.0) stable; urgency=medium
* Set TCP keepalive on sockets so broken connections are reaped (#33, !33) [ Patrick J Cherry ]
* Explicitly close the server control socket, and wait for it to close, to
prevent deadlocks during the server clean-up process (#40 !58)
* Ensure mirroring can be restarted after a break command is sent to the
source (#37, !59)
-- James Carter <james.carter@bytemark.co.uk> Fri, 11 Jan 2019 10:37:23 +0000
flexnbd (0.4.0) stable; urgency=medium
* Ensure proxy state is completely reset before upstream init is read,
ensuring any waiting requests are fully replayed (#39, !54)
-- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 15 Nov 2018 14:24:26 +0000
flexnbd (0.3.0) stable; urgency=medium
* Force a msync after every write, ignoring FUA flag, or lack thereof (!51).
-- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 24 Apr 2018 12:05:43 +0100
flexnbd (0.2.0) stable; urgency=medium
[ James Carter ]
* Set TCP keepalive on sockets so broken connections are reaped (#33, !33,
!36)
* Add a context to logs to make debugging problems easier (#34, !34) * Add a context to logs to make debugging problems easier (#34, !34)
-- James Carter <james.carter@bytemark.co.uk> Thu, 11 Jan 2018 10:05:35 +0000 [ Chris Cottam ]
* Increased NBD_MAX_SIZE from 1MB to 32MB for qemu 2.11 (!35)
[ Patrick J Cherry ]
* Added FLUSH and FUA support (!38)
* Server returns ENOSPC in response to writes beyond the end of the
filesystem, and EINVAL to unknown commands. (#36, !40)
* Proxy passes all NBD protocol errors through to the client instead of
disconnecting and retrying (#36, !40)
* Fix struct types in readwrite.c (#35, !41)
* Ignore ends of discs that stray outside of 512-byte sector sizes (!42).
* Tweak logging for readloop failures (!44)
* Alter semantics of NBD_MAX_BLOCK_SIZE to remove struct size overheads when
calculating if a request exceeds the max block size (!45)
* Added tests for setting TCP_NODELAY on upstream-reconnections in the
proxy, and refactored the other LD_PRELOAD tests (!43)
* Clean up dead threads before calculating the number of connected clients
on the status command (!46)
-- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 20 Feb 2018 11:43:22 +0000
flexnbd (0.1.7) stable; urgency=medium flexnbd (0.1.7) stable; urgency=medium

View File

@@ -45,8 +45,7 @@ int build_allocation_map(struct bitset * allocation_map, int fd)
if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) { if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
debug("Couldn't get fiemap, returning no allocation_map"); debug("Couldn't get fiemap, returning no allocation_map");
return 0; /* it's up to the caller to free the map */ return 0; /* it's up to the caller to free the map */
} } else {
else {
for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) { for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) {
bitset_set_range(allocation_map, bitset_set_range(allocation_map,
fiemap->fm_extents[i].fe_logical, fiemap->fm_extents[i].fe_logical,
@@ -58,12 +57,10 @@ int build_allocation_map(struct bitset * allocation_map, int fd)
* if we've actually hit max_offsets. * if we've actually hit max_offsets.
*/ */
if (fiemap->fm_mapped_extents > 0) { if (fiemap->fm_mapped_extents > 0) {
struct fiemap_extent *last = &fiemap->fm_extents[ struct fiemap_extent *last =
fiemap->fm_mapped_extents-1 &fiemap->fm_extents[fiemap->fm_mapped_extents - 1];
];
offset = last->fe_logical + last->fe_length; offset = last->fe_logical + last->fe_length;
} } else {
else {
offset += fiemap->fm_length; offset += fiemap->fm_length;
} }
} }
@@ -74,7 +71,8 @@ int build_allocation_map(struct bitset * allocation_map, int fd)
} }
int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **out_map) int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
void **out_map)
{ {
/* /*
* size and out_size are intentionally of different types. * size and out_size are intentionally of different types.
@@ -85,7 +83,7 @@ int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **
off64_t size; off64_t size;
/* O_DIRECT should not be used with mmap() */ /* O_DIRECT should not be used with mmap() */
*out_fd = open(filename, O_RDWR | O_SYNC ); *out_fd = open(filename, O_RDWR | O_NOATIME);
if (*out_fd < 1) { if (*out_fd < 1) {
warn("open(%s) failed: does it exist?", filename); warn("open(%s) failed: does it exist?", filename);
@@ -97,6 +95,15 @@ int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **
warn("lseek64() failed"); warn("lseek64() failed");
return size; return size;
} }
/* If discs are not in multiples of 512, then odd things happen,
* resulting in reads/writes past the ends of files.
*/
if (size != (size & (~0x1ff))) {
warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
size &= ~0x1ff;
}
if (out_size) { if (out_size) {
*out_size = size; *out_size = size;
} }
@@ -108,9 +115,9 @@ int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **
warn("mmap64() failed"); warn("mmap64() failed");
return -1; return -1;
} }
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd, *out_map); debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd,
} *out_map);
else { } else {
debug("opened %s size %ld on fd %d", filename, size, *out_fd); debug("opened %s size %ld on fd %d", filename, size, *out_fd);
} }
@@ -141,7 +148,8 @@ int readloop(int filedes, void *buffer, size_t size)
ssize_t result = read(filedes, buffer + readden, size - readden); ssize_t result = read(filedes, buffer + readden, size - readden);
if (result == 0 /* EOF */ ) { if (result == 0 /* EOF */ ) {
warn( "end-of-file detected while reading after %i bytes", readden ); warn("end-of-file detected while reading after %i bytes",
readden);
return -1; return -1;
} }
@@ -161,7 +169,9 @@ int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count)
size_t sent = 0; size_t sent = 0;
while (sent < count) { while (sent < count) {
ssize_t result = sendfile64(out_fd, in_fd, offset, count - sent); ssize_t result = sendfile64(out_fd, in_fd, offset, count - sent);
debug("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result); debug
("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld",
out_fd, in_fd, offset, count - sent, result);
if (result == -1) { if (result == -1) {
debug("%s (%i) calling sendfile64()", strerror(errno), errno); debug("%s (%i) calling sendfile64()", strerror(errno), errno);
@@ -175,7 +185,8 @@ int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count)
} }
#include <errno.h> #include <errno.h>
ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2) ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
loff_t * off_out, size_t len, unsigned int flags2)
{ {
const unsigned int flags = SPLICE_F_MORE | SPLICE_F_MOVE | flags2; const unsigned int flags = SPLICE_F_MORE | SPLICE_F_MOVE | flags2;
size_t spliced = 0; size_t spliced = 0;
@@ -183,13 +194,13 @@ ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_
//debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len); //debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len);
while (spliced < len) { while (spliced < len) {
ssize_t result = splice(fd_in, off_in, fd_out, off_out, len, flags); ssize_t result =
splice(fd_in, off_in, fd_out, off_out, len, flags);
if (result < 0) { if (result < 0) {
//debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len); //debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len);
if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK)) { if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK)) {
return spliced; return spliced;
} } else {
else {
return -1; return -1;
} }
} else { } else {
@@ -213,13 +224,19 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
while (spliced < len) { while (spliced < len) {
ssize_t run = len - spliced; ssize_t run = len - spliced;
ssize_t s2, s1 = spliceloop(fd_in, NULL, pipefd[1], NULL, run, SPLICE_F_NONBLOCK); ssize_t s2, s1 =
spliceloop(fd_in, NULL, pipefd[1], NULL, run,
SPLICE_F_NONBLOCK);
/*if (run > 65535) /*if (run > 65535)
run = 65535; */ run = 65535; */
if (s1 < 0) { break; } if (s1 < 0) {
break;
}
s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0); s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
if (s2 < 0) { break; } if (s2 < 0) {
break;
}
spliced += s2; spliced += s2;
} }
close(pipefd[0]); close(pipefd[0]);
@@ -241,7 +258,9 @@ int read_until_newline(int fd, char* buf, int bufsize)
for (cur = 0; cur < bufsize; cur++) { for (cur = 0; cur < bufsize; cur++) {
int result = read(fd, buf + cur, 1); int result = read(fd, buf + cur, 1);
if (result <= 0) { return -1; } if (result <= 0) {
return -1;
}
if (buf[cur] == 10) { if (buf[cur] == 10) {
buf[cur] = '\0'; buf[cur] = '\0';
break; break;
@@ -266,7 +285,9 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines)
* -1 for an eof * -1 for an eof
* -1 for a read error * -1 for a read error
*/ */
if (readden <= 1) { return lines_count; } if (readden <= 1) {
return lines_count;
}
*lines = xrealloc(*lines, (lines_count + 1) * sizeof(char *)); *lines = xrealloc(*lines, (lines_count + 1) * sizeof(char *));
(*lines)[lines_count] = strdup(line); (*lines)[lines_count] = strdup(line);
if ((*lines)[lines_count][0] == 0) { if ((*lines)[lines_count][0] == 0) {
@@ -304,7 +325,8 @@ ssize_t iobuf_read(int fd, struct iobuf *iobuf, size_t default_size )
} }
left = iobuf->size - iobuf->needle; left = iobuf->size - iobuf->needle;
debug( "Reading %"PRIu32" of %"PRIu32" bytes from fd %i", left, iobuf->size, fd ); debug("Reading %" PRIu32 " of %" PRIu32 " bytes from fd %i", left,
iobuf->size, fd);
count = read(fd, iobuf->buf + iobuf->needle, left); count = read(fd, iobuf->buf + iobuf->needle, left);
@@ -332,7 +354,8 @@ ssize_t iobuf_write( int fd, struct iobuf *iobuf )
size_t left = iobuf->size - iobuf->needle; size_t left = iobuf->size - iobuf->needle;
ssize_t count; ssize_t count;
debug( "Writing %"PRIu32" of %"PRIu32" bytes to fd %i", left, iobuf->size, fd ); debug("Writing %" PRIu32 " of %" PRIu32 " bytes to fd %i", left,
iobuf->size, fd);
count = write(fd, iobuf->buf + iobuf->needle, left); count = write(fd, iobuf->buf + iobuf->needle, left);
if (count >= 0) { if (count >= 0) {

View File

@@ -38,7 +38,8 @@ int readloop(int filedes, void *buffer, size_t size);
int sendfileloop(int out_fd, int in_fd, off64_t * offset, size_t count); int sendfileloop(int out_fd, int in_fd, off64_t * offset, size_t count);
/** Repeat a splice() operation until we have 'len' bytes. */ /** Repeat a splice() operation until we have 'len' bytes. */
ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2); ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
loff_t * off_out, size_t len, unsigned int flags2);
/** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary /** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary
* pipe and using the Linux splice call repeatedly until it has transferred * pipe and using the Linux splice call repeatedly until it has transferred
@@ -65,7 +66,8 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines);
* ''out_size'' and the address of the mmap in ''out_map''. If anything goes * ''out_size'' and the address of the mmap in ''out_map''. If anything goes
* wrong, returns -1 setting errno, otherwise 0. * wrong, returns -1 setting errno, otherwise 0.
*/ */
int open_and_mmap( const char* filename, int* out_fd, uint64_t* out_size, void **out_map); int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
void **out_map);
/** Check to see whether the given file descriptor is closed. /** Check to see whether the given file descriptor is closed.
@@ -73,4 +75,3 @@ int open_and_mmap( const char* filename, int* out_fd, uint64_t* out_size, void *
int fd_is_closed(int fd_in); int fd_is_closed(int fd_in);
#endif #endif

View File

@@ -94,4 +94,3 @@ void mode(char* mode, int argc, char **argv);
char *help_help_text; char *help_help_text;
#endif #endif

View File

@@ -13,6 +13,7 @@ void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to )
memcpy(to->passwd, from->passwd, 8); memcpy(to->passwd, from->passwd, 8);
to->magic = be64toh(from->magic); to->magic = be64toh(from->magic);
to->size = be64toh(from->size); to->size = be64toh(from->size);
to->flags = be32toh(from->flags);
} }
void nbd_h2r_init(struct nbd_init *from, struct nbd_init_raw *to) void nbd_h2r_init(struct nbd_init *from, struct nbd_init_raw *to)
@@ -20,39 +21,41 @@ void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to)
memcpy(to->passwd, from->passwd, 8); memcpy(to->passwd, from->passwd, 8);
to->magic = htobe64(from->magic); to->magic = htobe64(from->magic);
to->size = htobe64(from->size); to->size = htobe64(from->size);
to->flags = htobe32(from->flags);
} }
void nbd_r2h_request(struct nbd_request_raw *from, struct nbd_request *to) void nbd_r2h_request(struct nbd_request_raw *from, struct nbd_request *to)
{
to->magic = htobe32( from->magic );
to->type = htobe32( from->type );
to->handle.w = from->handle.w;
to->from = htobe64( from->from );
to->len = htobe32( from->len );
}
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to )
{ {
to->magic = be32toh(from->magic); to->magic = be32toh(from->magic);
to->type = be32toh( from->type ); to->flags = be16toh(from->flags);
to->type = be16toh(from->type);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
to->from = be64toh(from->from); to->from = be64toh(from->from);
to->len = be32toh(from->len); to->len = be32toh(from->len);
} }
void nbd_h2r_request(struct nbd_request *from, struct nbd_request_raw *to)
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to )
{ {
to->magic = htobe32(from->magic); to->magic = htobe32(from->magic);
to->error = htobe32( from->error ); to->flags = htobe16(from->flags);
to->type = htobe16(from->type);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
to->from = htobe64(from->from);
to->len = htobe32(from->len);
} }
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to )
void nbd_r2h_reply(struct nbd_reply_raw *from, struct nbd_reply *to)
{ {
to->magic = be32toh(from->magic); to->magic = be32toh(from->magic);
to->error = be32toh(from->error); to->error = be32toh(from->error);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
} }
void nbd_h2r_reply(struct nbd_reply *from, struct nbd_reply_raw *to)
{
to->magic = htobe32(from->magic);
to->error = htobe32(from->error);
to->handle.w = from->handle.w;
}

View File

@@ -7,15 +7,38 @@
#define INIT_MAGIC 0x0000420281861253 #define INIT_MAGIC 0x0000420281861253
#define REQUEST_MAGIC 0x25609513 #define REQUEST_MAGIC 0x25609513
#define REPLY_MAGIC 0x67446698 #define REPLY_MAGIC 0x67446698
#define REQUEST_READ 0 #define REQUEST_READ 0
#define REQUEST_WRITE 1 #define REQUEST_WRITE 1
#define REQUEST_DISCONNECT 2 #define REQUEST_DISCONNECT 2
#define REQUEST_FLUSH 3
/* The top 2 bytes of the type field are overloaded and can contain flags */ /* values for transmission flag field */
#define REQUEST_MASK 0x0000ffff #define FLAG_HAS_FLAGS (1 << 0) /* Flags are there */
#define FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */
#define FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */
/* values for command flag field */
#define CMD_FLAG_FUA (1 << 0)
#if 0
/* Not yet implemented by flexnbd */
#define REQUEST_TRIM 4
#define REQUEST_WRITE_ZEROES 6
#define FLAG_READ_ONLY (1 << 1) /* Device is read-only */
#define FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - rotational media */
#define FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */
#define FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send NBD_CMD_WRITE_ZEROES */
#define FLAG_CAN_MULTI_CONN (1 << 8) /* multiple connections are okay */
#define CMD_FLAG_NO_HOLE (1 << 1)
#endif
/* 1MiB is the de-facto standard for maximum size of header + data */ /* 32 MiB is the maximum qemu will send you:
* https://github.com/qemu/qemu/blob/v2.11.0/include/block/nbd.h#L183
*/
#define NBD_MAX_SIZE ( 32 * 1024 * 1024 ) #define NBD_MAX_SIZE ( 32 * 1024 * 1024 )
#define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) ) #define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) )
@@ -38,12 +61,14 @@ struct nbd_init_raw {
char passwd[8]; char passwd[8];
__be64 magic; __be64 magic;
__be64 size; __be64 size;
char reserved[128]; __be32 flags;
char reserved[124];
}; };
struct nbd_request_raw { struct nbd_request_raw {
__be32 magic; __be32 magic;
__be32 type; /* == READ || == WRITE */ __be16 flags;
__be16 type; /* == READ || == WRITE || == FLUSH */
nbd_handle_t handle; nbd_handle_t handle;
__be64 from; __be64 from;
__be32 len; __be32 len;
@@ -55,18 +80,18 @@ struct nbd_reply_raw {
nbd_handle_t handle; /* handle you got from request */ nbd_handle_t handle; /* handle you got from request */
}; };
struct nbd_init { struct nbd_init {
char passwd[8]; char passwd[8];
uint64_t magic; uint64_t magic;
uint64_t size; uint64_t size;
char reserved[128]; uint32_t flags;
char reserved[124];
}; };
struct nbd_request { struct nbd_request {
uint32_t magic; uint32_t magic;
uint32_t type; /* == READ || == WRITE || == DISCONNECT */ uint16_t flags;
uint16_t type; /* == READ || == WRITE || == DISCONNECT || == FLUSH */
nbd_handle_t handle; nbd_handle_t handle;
uint64_t from; uint64_t from;
uint32_t len; uint32_t len;
@@ -87,4 +112,3 @@ void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to );
void nbd_h2r_reply(struct nbd_reply *from, struct nbd_reply_raw *to); void nbd_h2r_reply(struct nbd_reply *from, struct nbd_reply_raw *to);
#endif #endif

View File

@@ -22,7 +22,9 @@ int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
/* allow user to start with [ and end with any other invalid char */ /* allow user to start with [ and end with any other invalid char */
{ {
int i = 0, j = 0; int i = 0, j = 0;
if (src[i] == '[') { i++; } if (src[i] == '[') {
i++;
}
for (; i < 64 && IS_IP_VALID_CHAR(src[i]); i++) { for (; i < 64 && IS_IP_VALID_CHAR(src[i]); i++) {
temp[j++] = src[i]; temp[j++] = src[i];
} }
@@ -72,8 +74,7 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
if (max == 0) { if (max == 0) {
*out = NULL; *out = NULL;
return 0; return 0;
} } else {
else {
list = xmalloc(max * sizeof(struct ip_and_mask)); list = xmalloc(max * sizeof(struct ip_and_mask));
*out = (struct ip_and_mask(*)[]) list; *out = (struct ip_and_mask(*)[]) list;
debug("acl alloc: %p", *out); debug("acl alloc: %p", *out);
@@ -88,16 +89,14 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
return i; return i;
} }
for (j=0; entries[i][j] && entries[i][j] != '/'; j++) for (j = 0; entries[i][j] && entries[i][j] != '/'; j++); // increment j!
; // increment j!
if (entries[i][j] == '/') { if (entries[i][j] == '/') {
outentry->mask = atoi(entries[i] + j + 1); outentry->mask = atoi(entries[i] + j + 1);
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) { if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) {
return i; return i;
} }
} } else {
else {
outentry->mask = MAX_MASK_BITS; outentry->mask = MAX_MASK_BITS;
} }
# undef MAX_MASK_BITS # undef MAX_MASK_BITS
@@ -124,4 +123,3 @@ void parse_port( char *s_port, struct sockaddr_in *out )
} }
out->sin_port = htobe16(raw_port); out->sin_port = htobe16(raw_port);
} }

View File

@@ -26,4 +26,3 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries);
void parse_port(char *s_port, struct sockaddr_in *out); void parse_port(char *s_port, struct sockaddr_in *out);
#endif #endif

View File

@@ -10,7 +10,9 @@
int socket_connect(struct sockaddr *to, struct sockaddr *from) int socket_connect(struct sockaddr *to, struct sockaddr *from)
{ {
int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0); int fd =
socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM,
0);
if (fd < 0) { if (fd < 0) {
warn("Couldn't create client socket"); warn("Couldn't create client socket");
return -1; return -1;
@@ -41,7 +43,8 @@ int socket_connect(struct sockaddr* to, struct sockaddr* from)
return fd; return fd;
} }
int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size ) int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
uint32_t * out_flags)
{ {
if (strncmp(init_raw->passwd, INIT_PASSWD, 8) != 0) { if (strncmp(init_raw->passwd, INIT_PASSWD, 8) != 0) {
warn("wrong passwd"); warn("wrong passwd");
@@ -56,13 +59,18 @@ int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size )
*out_size = be64toh(init_raw->size); *out_size = be64toh(init_raw->size);
} }
if (NULL != out_flags) {
*out_flags = be32toh(init_raw->flags);
}
return 1; return 1;
fail: fail:
return 0; return 0;
} }
int socket_nbd_read_hello( int fd, uint64_t* out_size ) int socket_nbd_read_hello(int fd, uint64_t * out_size,
uint32_t * out_flags)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
@@ -72,16 +80,18 @@ int socket_nbd_read_hello( int fd, uint64_t* out_size )
return 0; return 0;
} }
return nbd_check_hello( &init_raw, out_size ); return nbd_check_hello(&init_raw, out_size, out_flags);
} }
void nbd_hello_to_buf( struct nbd_init_raw *buf, off64_t out_size ) void nbd_hello_to_buf(struct nbd_init_raw *buf, off64_t out_size,
uint32_t out_flags)
{ {
struct nbd_init init; struct nbd_init init;
memcpy(&init.passwd, INIT_PASSWD, 8); memcpy(&init.passwd, INIT_PASSWD, 8);
init.magic = INIT_MAGIC; init.magic = INIT_MAGIC;
init.size = out_size; init.size = out_size;
init.flags = out_flags;
memset(buf, 0, sizeof(struct nbd_init_raw)); // ensure reserved is 0s memset(buf, 0, sizeof(struct nbd_init_raw)); // ensure reserved is 0s
nbd_h2r_init(&init, buf); nbd_h2r_init(&init, buf);
@@ -89,10 +99,10 @@ void nbd_hello_to_buf( struct nbd_init_raw *buf, off64_t out_size )
return; return;
} }
int socket_nbd_write_hello(int fd, off64_t out_size) int socket_nbd_write_hello(int fd, off64_t out_size, uint32_t out_flags)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
nbd_hello_to_buf( &init_raw, out_size ); nbd_hello_to_buf(&init_raw, out_size, out_flags);
if (0 > writeloop(fd, &init_raw, sizeof(init_raw))) { if (0 > writeloop(fd, &init_raw, sizeof(init_raw))) {
warn(SHOW_ERRNO("failed to write hello to socket")); warn(SHOW_ERRNO("failed to write hello to socket"));
@@ -101,20 +111,25 @@ int socket_nbd_write_hello(int fd, off64_t out_size)
return 1; return 1;
} }
void fill_request(struct nbd_request *request, int type, uint64_t from, uint32_t len) void fill_request(struct nbd_request_raw *request_raw, uint16_t type,
uint16_t flags, uint64_t from, uint32_t len)
{ {
request->magic = htobe32(REQUEST_MAGIC); request_raw->magic = htobe32(REQUEST_MAGIC);
request->type = htobe32(type); request_raw->type = htobe16(type);
request->handle.w = (((uint64_t)rand()) << 32) | ((uint64_t)rand()); request_raw->flags = htobe16(flags);
request->from = htobe64(from); request_raw->handle.w =
request->len = htobe32(len); (((uint64_t) rand()) << 32) | ((uint64_t) rand());
request_raw->from = htobe64(from);
request_raw->len = htobe32(len);
} }
void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply) void read_reply(int fd, uint64_t request_raw_handle,
struct nbd_reply *reply)
{ {
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
ERROR_IF_NEGATIVE(readloop(fd, &reply_raw, sizeof(struct nbd_reply_raw)), ERROR_IF_NEGATIVE(readloop
(fd, &reply_raw, sizeof(struct nbd_reply_raw)),
"Couldn't read reply"); "Couldn't read reply");
nbd_r2h_reply(&reply_raw, reply); nbd_r2h_reply(&reply_raw, reply);
@@ -125,7 +140,7 @@ void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply)
if (reply->error != 0) { if (reply->error != 0) {
error("Server replied with error %d", reply->error); error("Server replied with error %d", reply->error);
} }
if (request->handle.w != reply->handle.w) { if (request_raw_handle != reply_raw.handle.w) {
error("Did not reply with correct handle"); error("Did not reply with correct handle");
} }
} }
@@ -139,81 +154,77 @@ void wait_for_data( int fd, int timeout_secs )
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fd, &fds); FD_SET(fd, &fds);
selected = sock_try_select( selected =
FD_SETSIZE, &fds, NULL, NULL, timeout_secs >=0 ? &tv : NULL sock_try_select(FD_SETSIZE, &fds, NULL, NULL,
); timeout_secs >= 0 ? &tv : NULL);
FATAL_IF(-1 == selected, "Select failed"); FATAL_IF(-1 == selected, "Select failed");
ERROR_IF(0 == selected, "Timed out waiting for reply"); ERROR_IF(0 == selected, "Timed out waiting for reply");
} }
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs) void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
void *out_buf, int timeout_secs)
{ {
struct nbd_request request; struct nbd_request_raw request_raw;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request, REQUEST_READ, from, len); fill_request(&request_raw, REQUEST_READ, 0, from, len);
FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)), FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"Couldn't write request"); "Couldn't write request");
wait_for_data(fd, timeout_secs); wait_for_data(fd, timeout_secs);
read_reply(fd, &request, &reply); read_reply(fd, request_raw.handle.w, &reply);
if (out_buf) { if (out_buf) {
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), "Read failed");
"Read failed"); } else {
} FATAL_IF_NEGATIVE(splice_via_pipe_loop(fd, out_fd, len),
else { "Splice failed");
FATAL_IF_NEGATIVE(
splice_via_pipe_loop(fd, out_fd, len),
"Splice failed"
);
} }
} }
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd, void* in_buf, int timeout_secs) void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd,
void *in_buf, int timeout_secs)
{ {
struct nbd_request request; struct nbd_request_raw request_raw;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request, REQUEST_WRITE, from, len); fill_request(&request_raw, REQUEST_WRITE, 0, from, len);
ERROR_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)), ERROR_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"Couldn't write request"); "Couldn't write request");
if (in_buf) { if (in_buf) {
ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len), ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len), "Write failed");
"Write failed"); } else {
} ERROR_IF_NEGATIVE(splice_via_pipe_loop(in_fd, fd, len),
else { "Splice failed");
ERROR_IF_NEGATIVE(
splice_via_pipe_loop(in_fd, fd, len),
"Splice failed"
);
} }
wait_for_data(fd, timeout_secs); wait_for_data(fd, timeout_secs);
read_reply(fd, &request, &reply); read_reply(fd, request_raw.handle.w, &reply);
} }
int socket_nbd_disconnect(int fd) int socket_nbd_disconnect(int fd)
{ {
int success = 1; int success = 1;
struct nbd_request request; struct nbd_request_raw request_raw;
fill_request( &request, REQUEST_DISCONNECT, 0, 0 ); fill_request(&request_raw, REQUEST_DISCONNECT, 0, 0, 0);
/* FIXME: This shouldn't be a FATAL error. We should just drop /* FIXME: This shouldn't be a FATAL error. We should just drop
* the mirror without affecting the main server. * the mirror without affecting the main server.
*/ */
FATAL_IF_NEGATIVE( writeloop( fd, &request, sizeof( request ) ), FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"Failed to write the disconnect request."); "Failed to write the disconnect request.");
return success; return success;
} }
#define CHECK_RANGE(error_type) { \ #define CHECK_RANGE(error_type) { \
uint64_t size;\ uint64_t size;\
int success = socket_nbd_read_hello(params->client, &size); \ uint32_t flags;\
int success = socket_nbd_read_hello(params->client, &size, &flags); \
if ( success ) {\ if ( success ) {\
uint64_t endpoint = params->from + params->len; \ uint64_t endpoint = params->from + params->len; \
if (endpoint > size || \ if (endpoint > size || \
@@ -231,7 +242,9 @@ int socket_nbd_disconnect( int fd )
void do_read(struct mode_readwrite_params *params) void do_read(struct mode_readwrite_params *params)
{ {
params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic); params->client =
socket_connect(&params->connect_to.generic,
&params->connect_from.generic);
FATAL_IF_NEGATIVE(params->client, "Couldn't connect."); FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
CHECK_RANGE("read"); CHECK_RANGE("read");
socket_nbd_read(params->client, params->from, params->len, socket_nbd_read(params->client, params->from, params->len,
@@ -241,11 +254,12 @@ void do_read(struct mode_readwrite_params* params)
void do_write(struct mode_readwrite_params *params) void do_write(struct mode_readwrite_params *params)
{ {
params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic); params->client =
socket_connect(&params->connect_to.generic,
&params->connect_from.generic);
FATAL_IF_NEGATIVE(params->client, "Couldn't connect."); FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
CHECK_RANGE("write"); CHECK_RANGE("write");
socket_nbd_write(params->client, params->from, params->len, socket_nbd_write(params->client, params->from, params->len,
params->data_fd, NULL, 10); params->data_fd, NULL, 10);
close(params->client); close(params->client);
} }

View File

@@ -7,17 +7,20 @@
#include "nbdtypes.h" #include "nbdtypes.h"
int socket_connect(struct sockaddr *to, struct sockaddr *from); int socket_connect(struct sockaddr *to, struct sockaddr *from);
int socket_nbd_read_hello(int fd, uint64_t* size); int socket_nbd_read_hello(int fd, uint64_t * size, uint32_t * flags);
int socket_nbd_write_hello(int fd, uint64_t size); int socket_nbd_write_hello(int fd, uint64_t size, uint32_t flags);
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs); void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs); void *out_buf, int timeout_secs);
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int out_fd,
void *out_buf, int timeout_secs);
int socket_nbd_disconnect(int fd); int socket_nbd_disconnect(int fd);
/* as you can see, we're slowly accumulating code that should really be in an /* as you can see, we're slowly accumulating code that should really be in an
* NBD library */ * NBD library */
void nbd_hello_to_buf( struct nbd_init_raw* buf, uint64_t out_size ); void nbd_hello_to_buf(struct nbd_init_raw *buf, uint64_t out_size,
int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size ); uint32_t out_flags);
int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
uint32_t * out_flags);
#endif #endif

View File

@@ -24,7 +24,8 @@ void print_response( const char * response )
fprintf(out, "%s\n", response_text + 2); fprintf(out, "%s\n", response_text + 2);
} }
void do_remote_command(char* command, char* socket_name, int argc, char** argv) void do_remote_command(char *command, char *socket_name, int argc,
char **argv)
{ {
char newline = 10; char newline = 10;
int i; int i;
@@ -40,10 +41,10 @@ void do_remote_command(char* command, char* socket_name, int argc, char** argv)
address.sun_family = AF_UNIX; address.sun_family = AF_UNIX;
strncpy(address.sun_path, socket_name, sizeof(address.sun_path)); strncpy(address.sun_path, socket_name, sizeof(address.sun_path));
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(connect
connect(remote, (struct sockaddr*) &address, sizeof(address)), (remote, (struct sockaddr *) &address,
"Couldn't connect to %s", socket_name sizeof(address)), "Couldn't connect to %s",
); socket_name);
write(remote, command, strlen(command)); write(remote, command, strlen(command));
write(remote, &newline, 1); write(remote, &newline, 1);
@@ -55,13 +56,10 @@ void do_remote_command(char* command, char* socket_name, int argc, char** argv)
} }
write(remote, &newline, 1); write(remote, &newline, 1);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(read_until_newline(remote, response, max_response),
read_until_newline(remote, response, max_response), "Couldn't read response from %s", socket_name);
"Couldn't read response from %s", socket_name
);
print_response(response); print_response(response);
exit(atoi(response)); exit(atoi(response));
} }

View File

@@ -52,7 +52,9 @@ struct self_pipe * self_pipe_create(void)
struct self_pipe *sig = xmalloc(sizeof(struct self_pipe)); struct self_pipe *sig = xmalloc(sizeof(struct self_pipe));
int fds[2]; int fds[2];
if ( NULL == sig ) { return NULL; } if (NULL == sig) {
return NULL;
}
if (pipe(fds)) { if (pipe(fds)) {
free(sig); free(sig);
@@ -60,7 +62,8 @@ struct self_pipe * self_pipe_create(void)
return NULL; return NULL;
} }
if ( fcntl( fds[0], F_SETFL, O_NONBLOCK ) || fcntl( fds[1], F_SETFL, O_NONBLOCK ) ) { if (fcntl(fds[0], F_SETFL, O_NONBLOCK)
|| fcntl(fds[1], F_SETFL, O_NONBLOCK)) {
int fcntl_err = errno; int fcntl_err = errno;
while (close(fds[0]) == -1 && errno == EINTR); while (close(fds[0]) == -1 && errno == EINTR);
while (close(fds[1]) == -1 && errno == EINTR); while (close(fds[1]) == -1 && errno == EINTR);

View File

@@ -29,7 +29,8 @@ size_t sockaddr_size( const struct sockaddr* sa )
return ret; return ret;
} }
const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size_t len ) const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
size_t len)
{ {
NULLCHECK(sa); NULLCHECK(sa);
NULLCHECK(dest); NULLCHECK(dest);
@@ -65,7 +66,8 @@ const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size
int sock_set_reuseaddr(int fd, int optval) int sock_set_reuseaddr(int fd, int optval)
{ {
return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ); return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
} }
int sock_set_keepalive_params(int fd, int time, int intvl, int probes) int sock_set_keepalive_params(int fd, int time, int intvl, int probes)
@@ -81,28 +83,33 @@ int sock_set_keepalive_params( int fd, int time, int intvl, int probes)
int sock_set_keepalive(int fd, int optval) int sock_set_keepalive(int fd, int optval)
{ {
return setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) ); return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval,
sizeof(optval));
} }
int sock_set_tcp_keepidle(int fd, int optval) int sock_set_tcp_keepidle(int fd, int optval)
{ {
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval,
sizeof(optval));
} }
int sock_set_tcp_keepintvl(int fd, int optval) int sock_set_tcp_keepintvl(int fd, int optval)
{ {
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval,
sizeof(optval));
} }
int sock_set_tcp_keepcnt(int fd, int optval) int sock_set_tcp_keepcnt(int fd, int optval)
{ {
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &optval,
sizeof(optval));
} }
/* Set the tcp_nodelay option */ /* Set the tcp_nodelay option */
int sock_set_tcp_nodelay(int fd, int optval) int sock_set_tcp_nodelay(int fd, int optval)
{ {
return setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval,
sizeof(optval));
} }
int sock_set_tcp_cork(int fd, int optval) int sock_set_tcp_cork(int fd, int optval)
@@ -140,8 +147,7 @@ int sock_try_bind( int fd, const struct sockaddr* sa )
if (0 == bind_result) { if (0 == bind_result) {
info("Bound to %s", s_address); info("Bound to %s", s_address);
break; break;
} } else {
else {
warn(SHOW_ERRNO("Couldn't bind to %s", s_address)); warn(SHOW_ERRNO("Couldn't bind to %s", s_address));
switch (errno) { switch (errno) {
@@ -177,7 +183,8 @@ int sock_try_bind( int fd, const struct sockaddr* sa )
return bind_result; return bind_result;
} }
int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval *timeout)
{ {
int result; int result;
@@ -192,14 +199,16 @@ int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptf
return result; return result;
} }
int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait ) int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
int wait)
{ {
fd_set fds; fd_set fds;
struct timeval tv = { wait, 0 }; struct timeval tv = { wait, 0 };
int result = 0; int result = 0;
if (sock_set_nonblock(fd, 1) == -1) { if (sock_set_nonblock(fd, 1) == -1) {
warn( SHOW_ERRNO( "Failed to set socket non-blocking for connect()" ) ); warn(SHOW_ERRNO
("Failed to set socket non-blocking for connect()"));
return connect(fd, to, addrlen); return connect(fd, to, addrlen);
} }
@@ -284,4 +293,3 @@ int sock_try_close( int fd )
return result; return result;
} }

View File

@@ -12,7 +12,8 @@ size_t sockaddr_size(const struct sockaddr* sa);
/* Convert a sockaddr into an address. Like inet_ntop, it returns dest if /* Convert a sockaddr into an address. Like inet_ntop, it returns dest if
* successful, NULL otherwise. In the latter case, dest will contain "???" * successful, NULL otherwise. In the latter case, dest will contain "???"
*/ */
const char* sockaddr_address_string(const struct sockaddr* sa, char* dest, size_t len); const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
size_t len);
/* Configure TCP keepalive on a socket */ /* Configure TCP keepalive on a socket */
int sock_set_keepalive_params(int fd, int time, int intvl, int probes); int sock_set_keepalive_params(int fd, int time, int intvl, int probes);
@@ -44,13 +45,14 @@ int sock_set_nonblock(int fd, int optval);
int sock_try_bind(int fd, const struct sockaddr *sa); int sock_try_bind(int fd, const struct sockaddr *sa);
/* Try to call select(), retrying EINTR */ /* Try to call select(), retrying EINTR */
int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval *timeout);
/* Try to call connect(), timing out after wait seconds */ /* Try to call connect(), timing out after wait seconds */
int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait ); int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
int wait);
/* Try to call close(), retrying EINTR */ /* Try to call close(), retrying EINTR */
int sock_try_close(int fd); int sock_try_close(int fd);
#endif #endif

View File

@@ -26,10 +26,12 @@ void error_handler(int fatal)
if (context) { if (context) {
longjmp(context->jmp, fatal ? 1 : 2); longjmp(context->jmp, fatal ? 1 : 2);
} else {
if (fatal) {
abort();
} else {
pthread_exit((void *) 1);
} }
else {
if ( fatal ) { abort(); }
else { pthread_exit((void*) 1); }
} }
} }
@@ -43,7 +45,9 @@ void exit_err( const char *msg )
void mylog(int line_level, const char *format, ...) void mylog(int line_level, const char *format, ...)
{ {
if (line_level < log_level) { return; } if (line_level < log_level) {
return;
}
va_list argptr; va_list argptr;
@@ -57,8 +61,7 @@ uint64_t monotonic_time_ms()
struct timespec ts; struct timespec ts;
uint64_t seconds_ms, nanoseconds_ms; uint64_t seconds_ms, nanoseconds_ms;
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(clock_gettime(CLOCK_MONOTONIC, &ts),
clock_gettime(CLOCK_MONOTONIC, &ts),
SHOW_ERRNO("clock_gettime failed") SHOW_ERRNO("clock_gettime failed")
); );
@@ -75,7 +78,8 @@ uint64_t monotonic_time_ms()
void *xrealloc(void *ptr, size_t size) void *xrealloc(void *ptr, size_t size)
{ {
void *p = realloc(ptr, size); void *p = realloc(ptr, size);
FATAL_IF_NULL(p, "couldn't xrealloc %d bytes", ptr ? "realloc" : "malloc", size); FATAL_IF_NULL(p, "couldn't xrealloc %d bytes",
ptr ? "realloc" : "malloc", size);
return p; return p;
} }
@@ -85,4 +89,3 @@ void* xmalloc(size_t size)
memset(p, 0, size); memset(p, 0, size);
return p; return p;
} }

View File

@@ -13,7 +13,8 @@
void *xrealloc(void *ptr, size_t size); void *xrealloc(void *ptr, size_t size);
void *xmalloc(size_t size); void *xmalloc(size_t size);
typedef void (cleanup_handler)(void* /* context */, int /* is fatal? */); typedef void (cleanup_handler) (void * /* context */ ,
int /* is fatal? */ );
/* set from 0 - 5 depending on what level of verbosity you want */ /* set from 0 - 5 depending on what level of verbosity you want */
extern int log_level; extern int log_level;
@@ -162,4 +163,3 @@ uint64_t monotonic_time_ms(void);
#define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); } #define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); }
#endif #endif

View File

@@ -19,4 +19,3 @@ int main(int argc, char** argv)
return 0; return 0;
} }

View File

@@ -19,6 +19,7 @@ static struct option proxy_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE; static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
static char proxy_help_text[] = static char proxy_help_text[] =
"Usage: flexnbd-proxy <options>\n\n" "Usage: flexnbd-proxy <options>\n\n"
@@ -26,24 +27,24 @@ static char proxy_help_text[] =
"We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n" "We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n"
HELP_LINE HELP_LINE
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n" "\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
"\t--" OPT_PORT ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n" "\t--" OPT_PORT
",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n" "\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n" "\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
"\t--" OPT_BIND ",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--" OPT_BIND
"\t--" OPT_CACHE ",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n" ",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--"
QUIET_LINE OPT_CACHE
VERBOSE_LINE; ",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n"
QUIET_LINE VERBOSE_LINE;
static char proxy_default_cache_size[] = "4096"; static char proxy_default_cache_size[] = "4096";
void read_proxy_param( void read_proxy_param(int c,
int c,
char **downstream_addr, char **downstream_addr,
char **downstream_port, char **downstream_port,
char **upstream_addr, char **upstream_addr,
char **upstream_port, char **upstream_port,
char **bind_addr, char **bind_addr, char **cache_bytes)
char **cache_bytes)
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -116,34 +117,31 @@ int main( int argc, char *argv[] )
srand(time(NULL)); srand(time(NULL));
while (1) { while (1) {
c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL ); c = getopt_long(argc, argv, proxy_short_options, proxy_options,
if ( -1 == c ) { break; } NULL);
if (-1 == c) {
break;
}
read_proxy_param(c, read_proxy_param(c,
&downstream_addr, &downstream_addr,
&downstream_port, &downstream_port,
&upstream_addr, &upstream_addr,
&upstream_port, &upstream_port, &bind_addr, &cache_bytes);
&bind_addr,
&cache_bytes
);
} }
if (NULL == downstream_addr) { if (NULL == downstream_addr) {
fprintf(stderr, "--addr is required.\n"); fprintf(stderr, "--addr is required.\n");
exit_err(proxy_help_text); exit_err(proxy_help_text);
} else if (NULL == upstream_addr || NULL == upstream_port) { } else if (NULL == upstream_addr || NULL == upstream_port) {
fprintf( stderr, "both --conn-addr and --conn-port are required.\n" ); fprintf(stderr,
"both --conn-addr and --conn-port are required.\n");
exit_err(proxy_help_text); exit_err(proxy_help_text);
} }
proxy = proxy_create( proxy = proxy_create(downstream_addr,
downstream_addr,
downstream_port, downstream_port,
upstream_addr, upstream_addr,
upstream_port, upstream_port, bind_addr, cache_bytes);
bind_addr,
cache_bytes
);
/* Set these *after* proxy has been assigned to */ /* Set these *after* proxy has been assigned to */
sigaction(SIGTERM, &exit_action, NULL); sigaction(SIGTERM, &exit_action, NULL);
@@ -152,15 +150,12 @@ int main( int argc, char *argv[] )
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */ signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
if (NULL != downstream_port) { if (NULL != downstream_port) {
info( info("Proxying between %s %s (downstream) and %s %s (upstream)",
"Proxying between %s %s (downstream) and %s %s (upstream)", downstream_addr, downstream_port, upstream_addr,
downstream_addr, downstream_port, upstream_addr, upstream_port upstream_port);
);
} else { } else {
info( info("Proxying between %s (downstream) and %s %s (upstream)",
"Proxying between %s (downstream) and %s %s (upstream)", downstream_addr, upstream_addr, upstream_port);
downstream_addr, upstream_addr, upstream_port
);
} }
success = do_proxy(proxy); success = do_proxy(proxy);
@@ -168,4 +163,3 @@ int main( int argc, char *argv[] )
return success ? 0 : 1; return success ? 0 : 1;
} }

View File

@@ -2,7 +2,8 @@
#include "util.h" #include "util.h"
struct prefetch* prefetch_create( size_t size_bytes ){ struct prefetch *prefetch_create(size_t size_bytes)
{
struct prefetch *out = xmalloc(sizeof(struct prefetch)); struct prefetch *out = xmalloc(sizeof(struct prefetch));
NULLCHECK(out); NULLCHECK(out);
@@ -19,14 +20,16 @@ struct prefetch* prefetch_create( size_t size_bytes ){
} }
void prefetch_destroy( struct prefetch *prefetch ) { void prefetch_destroy(struct prefetch *prefetch)
{
if (prefetch) { if (prefetch) {
free(prefetch->buffer); free(prefetch->buffer);
free(prefetch); free(prefetch);
} }
} }
size_t prefetch_size( struct prefetch *prefetch){ size_t prefetch_size(struct prefetch *prefetch)
{
if (prefetch) { if (prefetch) {
return prefetch->size; return prefetch->size;
} else { } else {
@@ -34,21 +37,25 @@ size_t prefetch_size( struct prefetch *prefetch){
} }
} }
void prefetch_set_is_empty( struct prefetch *prefetch ){ void prefetch_set_is_empty(struct prefetch *prefetch)
{
prefetch_set_full(prefetch, 0); prefetch_set_full(prefetch, 0);
} }
void prefetch_set_is_full( struct prefetch *prefetch ){ void prefetch_set_is_full(struct prefetch *prefetch)
{
prefetch_set_full(prefetch, 1); prefetch_set_full(prefetch, 1);
} }
void prefetch_set_full( struct prefetch *prefetch, int val ){ void prefetch_set_full(struct prefetch *prefetch, int val)
{
if (prefetch) { if (prefetch) {
prefetch->is_full = val; prefetch->is_full = val;
} }
} }
int prefetch_is_full( struct prefetch *prefetch ){ int prefetch_is_full(struct prefetch *prefetch)
{
if (prefetch) { if (prefetch) {
return prefetch->is_full; return prefetch->is_full;
} else { } else {
@@ -56,13 +63,16 @@ int prefetch_is_full( struct prefetch *prefetch ){
} }
} }
int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len ){ int prefetch_contains(struct prefetch *prefetch, uint64_t from,
uint32_t len)
{
NULLCHECK(prefetch); NULLCHECK(prefetch);
return from >= prefetch->from && return from >= prefetch->from &&
from + len <= prefetch->from + prefetch->len; from + len <= prefetch->from + prefetch->len;
} }
char *prefetch_offset( struct prefetch *prefetch, uint64_t from ){ char *prefetch_offset(struct prefetch *prefetch, uint64_t from)
{
NULLCHECK(prefetch); NULLCHECK(prefetch);
return prefetch->buffer + (from - prefetch->from); return prefetch->buffer + (from - prefetch->from);
} }

View File

@@ -27,7 +27,8 @@ void prefetch_set_is_empty( struct prefetch *prefetch );
void prefetch_set_is_full(struct prefetch *prefetch); void prefetch_set_is_full(struct prefetch *prefetch);
void prefetch_set_full(struct prefetch *prefetch, int val); void prefetch_set_full(struct prefetch *prefetch, int val);
int prefetch_is_full(struct prefetch *prefetch); int prefetch_is_full(struct prefetch *prefetch);
int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len ); int prefetch_contains(struct prefetch *prefetch, uint64_t from,
uint32_t len);
char *prefetch_offset(struct prefetch *prefetch, uint64_t from); char *prefetch_offset(struct prefetch *prefetch, uint64_t from);
#endif #endif

View File

@@ -13,13 +13,11 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
struct proxier* proxy_create( struct proxier *proxy_create(char *s_downstream_address,
char* s_downstream_address,
char *s_downstream_port, char *s_downstream_port,
char *s_upstream_address, char *s_upstream_address,
char *s_upstream_port, char *s_upstream_port,
char* s_upstream_bind, char *s_upstream_bind, char *s_cache_bytes)
char* s_cache_bytes )
{ {
struct proxier *out; struct proxier *out;
out = xmalloc(sizeof(struct proxier)); out = xmalloc(sizeof(struct proxier));
@@ -27,10 +25,9 @@ struct proxier* proxy_create(
FATAL_IF_NULL(s_downstream_address, "Listen address not specified"); FATAL_IF_NULL(s_downstream_address, "Listen address not specified");
NULLCHECK(s_downstream_address); NULLCHECK(s_downstream_address);
FATAL_UNLESS( FATAL_UNLESS(parse_to_sockaddr
parse_to_sockaddr( &out->listen_on.generic, s_downstream_address ), (&out->listen_on.generic, s_downstream_address),
"Couldn't parse downstream address %s" "Couldn't parse downstream address %s");
);
if (out->listen_on.family != AF_UNIX) { if (out->listen_on.family != AF_UNIX) {
FATAL_IF_NULL(s_downstream_port, "Downstream port not specified"); FATAL_IF_NULL(s_downstream_port, "Downstream port not specified");
@@ -41,22 +38,19 @@ struct proxier* proxy_create(
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified"); FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
NULLCHECK(s_upstream_address); NULLCHECK(s_upstream_address);
FATAL_UNLESS( FATAL_UNLESS(parse_ip_to_sockaddr
parse_ip_to_sockaddr( &out->connect_to.generic, s_upstream_address ), (&out->connect_to.generic, s_upstream_address),
"Couldn't parse upstream address '%s'", "Couldn't parse upstream address '%s'",
s_upstream_address s_upstream_address);
);
FATAL_IF_NULL(s_upstream_port, "Upstream port not specified"); FATAL_IF_NULL(s_upstream_port, "Upstream port not specified");
NULLCHECK(s_upstream_port); NULLCHECK(s_upstream_port);
parse_port(s_upstream_port, &out->connect_to.v4); parse_port(s_upstream_port, &out->connect_to.v4);
if (s_upstream_bind) { if (s_upstream_bind) {
FATAL_IF_ZERO( FATAL_IF_ZERO(parse_ip_to_sockaddr
parse_ip_to_sockaddr( &out->connect_from.generic, s_upstream_bind ), (&out->connect_from.generic, s_upstream_bind),
"Couldn't parse bind address '%s'", "Couldn't parse bind address '%s'", s_upstream_bind);
s_upstream_bind
);
out->bind = 1; out->bind = 1;
} }
@@ -64,6 +58,20 @@ struct proxier* proxy_create(
out->downstream_fd = -1; out->downstream_fd = -1;
out->upstream_fd = -1; out->upstream_fd = -1;
int upstream_timeout = UPSTREAM_TIMEOUT;
char *env_upstream_timeout = getenv("FLEXNBD_UPSTREAM_TIMEOUT");
if (NULL != env_upstream_timeout) {
int ut = atoi(env_upstream_timeout);
warn("Got %i from atoi\n", ut);
if (ut > 0) {
upstream_timeout = ut;
}
}
out->upstream_timeout = upstream_timeout;
out->upstream_timeout_ms = (long unsigned int) upstream_timeout * 1000;
out->prefetch = NULL; out->prefetch = NULL;
if (s_cache_bytes) { if (s_cache_bytes) {
int cache_bytes = atoi(s_cache_bytes); int cache_bytes = atoi(s_cache_bytes);
@@ -83,18 +91,21 @@ struct proxier* proxy_create(
out->req.buf = xmalloc(NBD_MAX_SIZE + NBD_REQUEST_SIZE); out->req.buf = xmalloc(NBD_MAX_SIZE + NBD_REQUEST_SIZE);
out->rsp.buf = xmalloc(NBD_MAX_SIZE + NBD_REPLY_SIZE); out->rsp.buf = xmalloc(NBD_MAX_SIZE + NBD_REPLY_SIZE);
log_context = xmalloc( strlen(s_upstream_address) + strlen(s_upstream_port) + 2 ); log_context =
xmalloc(strlen(s_upstream_address) + strlen(s_upstream_port) + 2);
sprintf(log_context, "%s:%s", s_upstream_address, s_upstream_port); sprintf(log_context, "%s:%s", s_upstream_address, s_upstream_port);
return out; return out;
} }
int proxy_prefetches( struct proxier* proxy ) { int proxy_prefetches(struct proxier *proxy)
{
NULLCHECK(proxy); NULLCHECK(proxy);
return proxy->prefetch != NULL; return proxy->prefetch != NULL;
} }
int proxy_prefetch_bufsize( struct proxier* proxy ){ int proxy_prefetch_bufsize(struct proxier *proxy)
{
NULLCHECK(proxy); NULLCHECK(proxy);
return prefetch_size(proxy->prefetch); return prefetch_size(proxy->prefetch);
} }
@@ -110,7 +121,8 @@ void proxy_destroy( struct proxier* proxy )
} }
/* Shared between our two different connect_to_upstream paths */ /* Shared between our two different connect_to_upstream paths */
void proxy_finish_connect_to_upstream( struct proxier *proxy, uint64_t size ); void proxy_finish_connect_to_upstream(struct proxier *proxy, uint64_t size,
uint32_t flags);
/* Try to establish a connection to our upstream server. Return 1 on success, /* Try to establish a connection to our upstream server. Return 1 on success,
* 0 on failure. this is a blocking call that returns a non-blocking socket. * 0 on failure. this is a blocking call that returns a non-blocking socket.
@@ -124,22 +136,22 @@ int proxy_connect_to_upstream( struct proxier* proxy )
int fd = socket_connect(&proxy->connect_to.generic, connect_from); int fd = socket_connect(&proxy->connect_to.generic, connect_from);
uint64_t size = 0; uint64_t size = 0;
uint32_t flags = 0;
if (-1 == fd) { if (-1 == fd) {
return 0; return 0;
} }
if( !socket_nbd_read_hello( fd, &size ) ) { if (!socket_nbd_read_hello(fd, &size, &flags)) {
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(fd),
sock_try_close( fd ), "Couldn't close() after failed read of NBD hello on fd %i",
"Couldn't close() after failed read of NBD hello on fd %i", fd fd);
);
return 0; return 0;
} }
proxy->upstream_fd = fd; proxy->upstream_fd = fd;
sock_set_nonblock(fd, 1); sock_set_nonblock(fd, 1);
proxy_finish_connect_to_upstream( proxy, size ); proxy_finish_connect_to_upstream(proxy, size, flags);
return 1; return 1;
} }
@@ -160,7 +172,8 @@ void proxy_start_connect_to_upstream( struct proxier* proxy )
fd = socket(to->sa_family, SOCK_STREAM, 0); fd = socket(to->sa_family, SOCK_STREAM, 0);
if (fd < 0) { if (fd < 0) {
warn( SHOW_ERRNO( "Couldn't create socket to reconnect to upstream" ) ); warn(SHOW_ERRNO
("Couldn't create socket to reconnect to upstream"));
return; return;
} }
@@ -195,19 +208,28 @@ error:
return; return;
} }
void proxy_finish_connect_to_upstream( struct proxier *proxy, uint64_t size ) { void proxy_finish_connect_to_upstream(struct proxier *proxy, uint64_t size,
uint32_t flags)
{
if (proxy->upstream_size == 0) { if (proxy->upstream_size == 0) {
info("Size of upstream image is %" PRIu64 " bytes", size); info("Size of upstream image is %" PRIu64 " bytes", size);
} else if (proxy->upstream_size != size) { } else if (proxy->upstream_size != size) {
warn( warn("Size changed from %" PRIu64 " to %" PRIu64 " bytes",
"Size changed from %"PRIu64" to %"PRIu64" bytes", proxy->upstream_size, size);
proxy->upstream_size, size
);
} }
proxy->upstream_size = size; proxy->upstream_size = size;
if (proxy->upstream_flags == 0) {
info("Upstream transmission flags set to %" PRIu32 "", flags);
} else if (proxy->upstream_flags != flags) {
warn("Upstream transmission flags changed from %" PRIu32 " to %"
PRIu32 "", proxy->upstream_flags, flags);
}
proxy->upstream_flags = flags;
if (AF_UNIX != proxy->connect_to.family) { if (AF_UNIX != proxy->connect_to.family) {
if (sock_set_tcp_nodelay(proxy->upstream_fd, 1) == -1) { if (sock_set_tcp_nodelay(proxy->upstream_fd, 1) == -1) {
warn(SHOW_ERRNO("Failed to set TCP_NODELAY")); warn(SHOW_ERRNO("Failed to set TCP_NODELAY"));
@@ -225,11 +247,9 @@ void proxy_disconnect_from_upstream( struct proxier* proxy )
info("Closing upstream connection on fd %i", proxy->upstream_fd); info("Closing upstream connection on fd %i", proxy->upstream_fd);
/* TODO: An NBD disconnect would be pleasant here */ /* TODO: An NBD disconnect would be pleasant here */
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(proxy->upstream_fd),
sock_try_close( proxy->upstream_fd ),
"Failed to close() fd %i when disconnecting from upstream", "Failed to close() fd %i when disconnecting from upstream",
proxy->upstream_fd proxy->upstream_fd);
);
proxy->upstream_fd = -1; proxy->upstream_fd = -1;
} }
} }
@@ -241,31 +261,28 @@ void proxy_open_listen_socket(struct proxier* params)
NULLCHECK(params); NULLCHECK(params);
params->listen_fd = socket(params->listen_on.family, SOCK_STREAM, 0); params->listen_fd = socket(params->listen_on.family, SOCK_STREAM, 0);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(params->listen_fd,
params->listen_fd, SHOW_ERRNO( "Couldn't create listen socket" ) SHOW_ERRNO("Couldn't create listen socket")
); );
/* Allow us to restart quickly */ /* Allow us to restart quickly */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_set_reuseaddr(params->listen_fd, 1),
sock_set_reuseaddr(params->listen_fd, 1),
SHOW_ERRNO("Couldn't set SO_REUSEADDR") SHOW_ERRNO("Couldn't set SO_REUSEADDR")
); );
if (AF_UNIX != params->listen_on.family) { if (AF_UNIX != params->listen_on.family) {
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_set_tcp_nodelay(params->listen_fd, 1),
sock_set_tcp_nodelay(params->listen_fd, 1),
SHOW_ERRNO("Couldn't set TCP_NODELAY") SHOW_ERRNO("Couldn't set TCP_NODELAY")
); );
} }
FATAL_UNLESS_ZERO( FATAL_UNLESS_ZERO(sock_try_bind
sock_try_bind( params->listen_fd, &params->listen_on.generic ), (params->listen_fd, &params->listen_on.generic),
SHOW_ERRNO("Failed to bind to listening socket") SHOW_ERRNO("Failed to bind to listening socket")
); );
/* We're only serving one client at a time, hence backlog of 1 */ /* We're only serving one client at a time, hence backlog of 1 */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(listen(params->listen_fd, 1),
listen(params->listen_fd, 1),
SHOW_ERRNO("Failed to listen on listening socket") SHOW_ERRNO("Failed to listen on listening socket")
); );
@@ -296,8 +313,8 @@ static char* proxy_session_state_names[] = {
static inline int proxy_state_upstream(int state) static inline int proxy_state_upstream(int state)
{ {
return state == CONNECT_TO_UPSTREAM || state == READ_INIT_FROM_UPSTREAM || return state == CONNECT_TO_UPSTREAM || state == READ_INIT_FROM_UPSTREAM
state == WRITE_TO_UPSTREAM || state == READ_FROM_UPSTREAM; || state == WRITE_TO_UPSTREAM || state == READ_FROM_UPSTREAM;
} }
int proxy_prefetch_for_request(struct proxier *proxy, int state) int proxy_prefetch_for_request(struct proxier *proxy, int state)
@@ -306,10 +323,12 @@ int proxy_prefetch_for_request( struct proxier* proxy, int state )
struct nbd_request *req = &proxy->req_hdr; struct nbd_request *req = &proxy->req_hdr;
struct nbd_reply *rsp = &proxy->rsp_hdr; struct nbd_reply *rsp = &proxy->rsp_hdr;
struct nbd_request_raw* req_raw = (struct nbd_request_raw*) proxy->req.buf; struct nbd_request_raw *req_raw =
struct nbd_reply_raw *rsp_raw = (struct nbd_reply_raw*) proxy->rsp.buf; (struct nbd_request_raw *) proxy->req.buf;
struct nbd_reply_raw *rsp_raw =
(struct nbd_reply_raw *) proxy->rsp.buf;
int is_read = ( req->type & REQUEST_MASK ) == REQUEST_READ; int is_read = req->type == REQUEST_READ;
if (is_read) { if (is_read) {
/* See if we can respond with what's in our prefetch /* See if we can respond with what's in our prefetch
@@ -328,11 +347,8 @@ int proxy_prefetch_for_request( struct proxier* proxy, int state )
nbd_h2r_reply(rsp, rsp_raw); nbd_h2r_reply(rsp, rsp_raw);
/* and the data */ /* and the data */
memcpy( memcpy(proxy->rsp.buf + NBD_REPLY_SIZE,
proxy->rsp.buf + NBD_REPLY_SIZE, prefetch_offset(proxy->prefetch, req->from), req->len);
prefetch_offset( proxy->prefetch, req->from ),
req->len
);
proxy->rsp.size = NBD_REPLY_SIZE + req->len; proxy->rsp.size = NBD_REPLY_SIZE + req->len;
proxy->rsp.needle = 0; proxy->rsp.needle = 0;
@@ -340,14 +356,14 @@ int proxy_prefetch_for_request( struct proxier* proxy, int state )
/* return early, our work here is done */ /* return early, our work here is done */
return WRITE_TO_DOWNSTREAM; return WRITE_TO_DOWNSTREAM;
} }
} } else {
else {
/* Safety catch. If we're sending a write request, we /* Safety catch. If we're sending a write request, we
* blow away the cache. This is very pessimistic, but * blow away the cache. This is very pessimistic, but
* it's simpler (and therefore safer) than working out * it's simpler (and therefore safer) than working out
* whether we can keep it or not. * whether we can keep it or not.
*/ */
debug( "Blowing away prefetch cache on type %d request.", req->type ); debug("Blowing away prefetch cache on type %d request.",
req->type);
prefetch_set_is_empty(proxy->prefetch); prefetch_set_is_empty(proxy->prefetch);
} }
@@ -395,13 +411,12 @@ int proxy_prefetch_for_reply( struct proxier* proxy, int state )
prefetched_bytes = proxy->req_hdr.len - proxy->prefetch_req_orig_len; prefetched_bytes = proxy->req_hdr.len - proxy->prefetch_req_orig_len;
debug("Prefetched additional %d bytes", prefetched_bytes); debug("Prefetched additional %d bytes", prefetched_bytes);
memcpy( memcpy(proxy->prefetch->buffer,
proxy->prefetch->buffer,
proxy->rsp.buf + proxy->prefetch_req_orig_len + NBD_REPLY_SIZE, proxy->rsp.buf + proxy->prefetch_req_orig_len + NBD_REPLY_SIZE,
prefetched_bytes prefetched_bytes);
);
proxy->prefetch->from = proxy->req_hdr.from + proxy->prefetch_req_orig_len; proxy->prefetch->from =
proxy->req_hdr.from + proxy->prefetch_req_orig_len;
proxy->prefetch->len = prefetched_bytes; proxy->prefetch->len = prefetched_bytes;
/* We've finished with proxy->req by now, so don't need to alter it to make /* We've finished with proxy->req by now, so don't need to alter it to make
@@ -424,12 +439,14 @@ int proxy_read_from_downstream( struct proxier *proxy, int state )
{ {
ssize_t count; ssize_t count;
struct nbd_request_raw* request_raw = (struct nbd_request_raw*) proxy->req.buf; struct nbd_request_raw *request_raw =
(struct nbd_request_raw *) proxy->req.buf;
struct nbd_request *request = &(proxy->req_hdr); struct nbd_request *request = &(proxy->req_hdr);
// assert( state == READ_FROM_DOWNSTREAM ); // assert( state == READ_FROM_DOWNSTREAM );
count = iobuf_read( proxy->downstream_fd, &proxy->req, NBD_REQUEST_SIZE ); count =
iobuf_read(proxy->downstream_fd, &proxy->req, NBD_REQUEST_SIZE);
if (count == -1) { if (count == -1) {
warn(SHOW_ERRNO("Couldn't read request from downstream")); warn(SHOW_ERRNO("Couldn't read request from downstream"));
@@ -439,7 +456,7 @@ int proxy_read_from_downstream( struct proxier *proxy, int state )
if (proxy->req.needle == NBD_REQUEST_SIZE) { if (proxy->req.needle == NBD_REQUEST_SIZE) {
nbd_r2h_request(request_raw, request); nbd_r2h_request(request_raw, request);
if ( ( request->type & REQUEST_MASK ) == REQUEST_DISCONNECT ) { if (request->type == REQUEST_DISCONNECT) {
info("Received disconnect request from client"); info("Received disconnect request from client");
return EXIT; return EXIT;
} }
@@ -448,15 +465,17 @@ int proxy_read_from_downstream( struct proxier *proxy, int state )
* been taken into account in the xmalloc, so no need to worry * been taken into account in the xmalloc, so no need to worry
* about them here * about them here
*/ */
if ( ( request->type & REQUEST_MASK ) == REQUEST_READ ) { if (request->type == REQUEST_READ) {
if (request->len > NBD_MAX_SIZE) { if (request->len > NBD_MAX_SIZE) {
warn( "NBD read request size %"PRIu32" too large", request->len ); warn("NBD read request size %" PRIu32 " too large",
request->len);
return EXIT; return EXIT;
} }
} }
if ( (request->type & REQUEST_MASK ) == REQUEST_WRITE ) { if (request->type == REQUEST_WRITE) {
if (request->len > NBD_MAX_SIZE) { if (request->len > NBD_MAX_SIZE) {
warn( "NBD write request size %"PRIu32" too large", request->len ); warn("NBD write request size %" PRIu32 " too large",
request->len);
return EXIT; return EXIT;
} }
@@ -465,10 +484,9 @@ int proxy_read_from_downstream( struct proxier *proxy, int state )
} }
if (proxy->req.needle == proxy->req.size) { if (proxy->req.needle == proxy->req.size) {
debug( debug("Received NBD request from downstream. type=%" PRIu16
"Received NBD request from downstream. type=%"PRIu32" from=%"PRIu64" len=%"PRIu32, " flags=%" PRIu16 " from=%" PRIu64 " len=%" PRIu32,
request->type, request->from, request->len request->type, request->flags, request->from, request->len);
);
/* Finished reading, so advance state. Leave size untouched so the next /* Finished reading, so advance state. Leave size untouched so the next
* state knows how many bytes to write */ * state knows how many bytes to write */
@@ -486,9 +504,8 @@ int proxy_continue_connecting_to_upstream( struct proxier* proxy, int state )
// assert( state == CONNECT_TO_UPSTREAM ); // assert( state == CONNECT_TO_UPSTREAM );
result = getsockopt( result =
proxy->upstream_fd, SOL_SOCKET, SO_ERROR, &error, &len getsockopt(proxy->upstream_fd, SOL_SOCKET, SO_ERROR, &error, &len);
);
if (result == -1) { if (result == -1) {
warn(SHOW_ERRNO("Failed to tell if connected to upstream")); warn(SHOW_ERRNO("Failed to tell if connected to upstream"));
@@ -504,6 +521,20 @@ int proxy_continue_connecting_to_upstream( struct proxier* proxy, int state )
/* Data may have changed while we were disconnected */ /* Data may have changed while we were disconnected */
prefetch_set_is_empty(proxy->prefetch); prefetch_set_is_empty(proxy->prefetch);
/* Reset our needles and sizes.
*
* Don't zero the req buffer size in case there's an outstanding request
* waiting to be re-sent following a disconnection. The init and rsp
* buffers are used for reading from upstream. If we're in this state then
* any upstream reads will be re-requested.
*/
proxy->init.needle = 0;
proxy->init.size = 0;
proxy->rsp.needle = 0;
proxy->rsp.size = 0;
proxy->req.needle = 0;
/* Don't zero the req.size, as we may need to re-write it upstream */
info("Connected to upstream on fd %i", proxy->upstream_fd); info("Connected to upstream on fd %i", proxy->upstream_fd);
return READ_INIT_FROM_UPSTREAM; return READ_INIT_FROM_UPSTREAM;
} }
@@ -514,33 +545,40 @@ int proxy_read_init_from_upstream( struct proxier* proxy, int state )
// assert( state == READ_INIT_FROM_UPSTREAM ); // assert( state == READ_INIT_FROM_UPSTREAM );
count = iobuf_read( proxy->upstream_fd, &proxy->init, sizeof( struct nbd_init_raw ) ); count =
iobuf_read(proxy->upstream_fd, &proxy->init,
sizeof(struct nbd_init_raw));
if (count == -1) { if (count == -1) {
warn(SHOW_ERRNO("Failed to read init from upstream")); warn(SHOW_ERRNO("Failed to read init from upstream"));
goto disconnect; return CONNECT_TO_UPSTREAM;
} }
if (proxy->init.needle == proxy->init.size) { if (proxy->init.needle == proxy->init.size) {
uint64_t upstream_size; uint64_t upstream_size;
if ( !nbd_check_hello( (struct nbd_init_raw*) proxy->init.buf, &upstream_size ) ) { uint32_t upstream_flags;
if (!nbd_check_hello
((struct nbd_init_raw *) proxy->init.buf, &upstream_size,
&upstream_flags)) {
warn("Upstream sent invalid init"); warn("Upstream sent invalid init");
goto disconnect; return CONNECT_TO_UPSTREAM;
} }
/* Currently, we only get disconnected from upstream (so needing to come /* record the flags, and log the reconnection, set TCP_NODELAY */
* here) when we have an outstanding request. If that becomes false, proxy_finish_connect_to_upstream(proxy, upstream_size,
* we'll need to choose the right state to return to here */ upstream_flags);
/* Finished reading the init response now, reset the needle. */
proxy->init.needle = 0; proxy->init.needle = 0;
/* Currently, we only get disconnected from upstream (so needing to
* come here) when we have an outstanding request. If that becomes
* false, we'll need to choose the right state to return to here.
*/
return WRITE_TO_UPSTREAM; return WRITE_TO_UPSTREAM;
} }
return state; return state;
disconnect:
proxy->init.needle = 0;
proxy->init.size = 0;
return CONNECT_TO_UPSTREAM;
} }
int proxy_write_to_upstream(struct proxier *proxy, int state) int proxy_write_to_upstream(struct proxier *proxy, int state)
@@ -562,7 +600,6 @@ int proxy_write_to_upstream( struct proxier* proxy, int state )
if (count == -1) { if (count == -1) {
warn(SHOW_ERRNO("Failed to send request to upstream")); warn(SHOW_ERRNO("Failed to send request to upstream"));
proxy->req.needle = 0;
// We're throwing the socket away so no need to uncork // We're throwing the socket away so no need to uncork
return CONNECT_TO_UPSTREAM; return CONNECT_TO_UPSTREAM;
} }
@@ -591,14 +628,15 @@ int proxy_read_from_upstream( struct proxier* proxy, int state )
ssize_t count; ssize_t count;
struct nbd_reply *reply = &(proxy->rsp_hdr); struct nbd_reply *reply = &(proxy->rsp_hdr);
struct nbd_reply_raw* reply_raw = (struct nbd_reply_raw*) proxy->rsp.buf; struct nbd_reply_raw *reply_raw =
(struct nbd_reply_raw *) proxy->rsp.buf;
/* We can't assume the NBD_REPLY_SIZE + req->len is what we'll get back */ /* We can't assume the NBD_REPLY_SIZE + req->len is what we'll get back */
count = iobuf_read(proxy->upstream_fd, &proxy->rsp, NBD_REPLY_SIZE); count = iobuf_read(proxy->upstream_fd, &proxy->rsp, NBD_REPLY_SIZE);
if (count == -1) { if (count == -1) {
warn(SHOW_ERRNO("Failed to get reply from upstream")); warn(SHOW_ERRNO("Failed to get reply from upstream"));
goto disconnect; return CONNECT_TO_UPSTREAM;
} }
if (proxy->rsp.needle == NBD_REPLY_SIZE) { if (proxy->rsp.needle == NBD_REPLY_SIZE) {
@@ -606,15 +644,10 @@ int proxy_read_from_upstream( struct proxier* proxy, int state )
if (reply->magic != REPLY_MAGIC) { if (reply->magic != REPLY_MAGIC) {
warn("Reply magic is incorrect"); warn("Reply magic is incorrect");
goto disconnect; return CONNECT_TO_UPSTREAM;
} }
if ( reply->error != 0 ) { if (proxy->req_hdr.type == REQUEST_READ) {
warn( "NBD error returned from upstream: %"PRIu32, reply->error );
goto disconnect;
}
if ( ( proxy->req_hdr.type & REQUEST_MASK ) == REQUEST_READ ) {
/* Get the read reply data too. */ /* Get the read reply data too. */
proxy->rsp.size += proxy->req_hdr.len; proxy->rsp.size += proxy->req_hdr.len;
} }
@@ -627,11 +660,6 @@ int proxy_read_from_upstream( struct proxier* proxy, int state )
} }
return state; return state;
disconnect:
proxy->rsp.needle = 0;
proxy->rsp.size = 0;
return CONNECT_TO_UPSTREAM;
} }
@@ -690,7 +718,8 @@ void proxy_session( struct proxier* proxy )
/* First action: Write hello to downstream */ /* First action: Write hello to downstream */
nbd_hello_to_buf( (struct nbd_init_raw *) proxy->rsp.buf, proxy->upstream_size ); nbd_hello_to_buf((struct nbd_init_raw *) proxy->rsp.buf,
proxy->upstream_size, proxy->upstream_flags);
proxy->rsp.size = sizeof(struct nbd_init_raw); proxy->rsp.size = sizeof(struct nbd_init_raw);
proxy->rsp.needle = 0; proxy->rsp.needle = 0;
state = WRITE_TO_DOWNSTREAM; state = WRITE_TO_DOWNSTREAM;
@@ -718,13 +747,13 @@ void proxy_session( struct proxier* proxy )
if (state != old_state) { if (state != old_state) {
state_started = monotonic_time_ms(); state_started = monotonic_time_ms();
debug( debug("State transition from %s to %s",
"State transition from %s to %s",
proxy_session_state_names[old_state], proxy_session_state_names[old_state],
proxy_session_state_names[state] proxy_session_state_names[state]
); );
} else { } else {
debug( "Proxy is in state %s", proxy_session_state_names[state], state ); debug("Proxy is in state %s", proxy_session_state_names[state],
state);
} }
old_state = state; old_state = state;
@@ -766,10 +795,15 @@ void proxy_session( struct proxier* proxy )
}; };
if (select_timeout.tv_sec > 0) { if (select_timeout.tv_sec > 0) {
if (select_timeout.tv_sec > proxy->upstream_timeout) {
select_timeout.tv_sec = proxy->upstream_timeout;
}
select_timeout_ptr = &select_timeout; select_timeout_ptr = &select_timeout;
} }
result = sock_try_select( FD_SETSIZE, &rfds, &wfds, NULL, select_timeout_ptr ); result =
sock_try_select(FD_SETSIZE, &rfds, &wfds, NULL,
select_timeout_ptr);
if (result == -1) { if (result == -1) {
warn(SHOW_ERRNO("select() failed: ")); warn(SHOW_ERRNO("select() failed: "));
@@ -795,7 +829,8 @@ void proxy_session( struct proxier* proxy )
break; break;
case CONNECT_TO_UPSTREAM: case CONNECT_TO_UPSTREAM:
if (FD_ISSET(proxy->upstream_fd, &wfds)) { if (FD_ISSET(proxy->upstream_fd, &wfds)) {
state = proxy_continue_connecting_to_upstream( proxy, state ); state =
proxy_continue_connecting_to_upstream(proxy, state);
} }
/* Leaving state untouched will retry connecting to upstream - /* Leaving state untouched will retry connecting to upstream -
* so introduce a bit of sleep */ * so introduce a bit of sleep */
@@ -836,27 +871,15 @@ void proxy_session( struct proxier* proxy )
/* In these states, we're interested in restarting after a timeout. /* In these states, we're interested in restarting after a timeout.
*/ */
if (old_state == state && proxy_state_upstream(state)) { if (old_state == state && proxy_state_upstream(state)) {
if ( ( monotonic_time_ms() ) - state_started > UPSTREAM_TIMEOUT ) { if ((monotonic_time_ms()) - state_started > proxy->upstream_timeout_ms) {
warn( warn("Timed out in state %s while communicating with upstream", proxy_session_state_names[state]);
"Timed out in state %s while communicating with upstream",
proxy_session_state_names[state]
);
state = CONNECT_TO_UPSTREAM; state = CONNECT_TO_UPSTREAM;
/* Since we've timed out, we won't have gone through the timeout logic
* in the various state handlers that resets these appropriately... */
proxy->init.size = 0;
proxy->init.needle = 0;
proxy->rsp.size = 0;
proxy->rsp.needle = 0;
} }
} }
} }
info( info("Finished proxy session on fd %i after %" PRIu64
"Finished proxy session on fd %i after %"PRIu64" successful request(s)", " successful request(s)", proxy->downstream_fd, proxy->req_count);
proxy->downstream_fd, proxy->req_count
);
/* Reset these two for the next session */ /* Reset these two for the next session */
proxy->req_count = 0; proxy->req_count = 0;
@@ -881,13 +904,13 @@ int proxy_accept( struct proxier* params )
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(params->listen_fd, &fds); FD_SET(params->listen_fd, &fds);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
SHOW_ERRNO("select() failed") SHOW_ERRNO("select() failed")
); );
if (FD_ISSET(params->listen_fd, &fds)) { if (FD_ISSET(params->listen_fd, &fds)) {
client_fd = accept( params->listen_fd, &client_address.generic, &socklen ); client_fd =
accept(params->listen_fd, &client_address.generic, &socklen);
if (client_address.family != AF_UNIX) { if (client_address.family != AF_UNIX) {
if (sock_set_tcp_nodelay(client_fd, 1) == -1) { if (sock_set_tcp_nodelay(client_fd, 1) == -1) {
@@ -900,11 +923,9 @@ int proxy_accept( struct proxier* params )
params->downstream_fd = client_fd; params->downstream_fd = client_fd;
proxy_session(params); proxy_session(params);
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(params->downstream_fd),
sock_try_close( params->downstream_fd ),
"Couldn't close() downstram fd %i after proxy session", "Couldn't close() downstram fd %i after proxy session",
params->downstream_fd params->downstream_fd);
);
params->downstream_fd = -1; params->downstream_fd = -1;
} }
@@ -929,33 +950,31 @@ void proxy_cleanup( struct proxier* proxy )
if (AF_UNIX == proxy->listen_on.family) { if (AF_UNIX == proxy->listen_on.family) {
if (-1 == unlink(proxy->listen_on.un.sun_path)) { if (-1 == unlink(proxy->listen_on.un.sun_path)) {
warn( SHOW_ERRNO( "Failed to unlink %s", proxy->listen_on.un.sun_path ) ); warn(SHOW_ERRNO
("Failed to unlink %s",
proxy->listen_on.un.sun_path));
} }
} }
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(proxy->listen_fd),
sock_try_close( proxy->listen_fd ), SHOW_ERRNO("Failed to close() listen fd %i",
SHOW_ERRNO( "Failed to close() listen fd %i", proxy->listen_fd ) proxy->listen_fd)
); );
proxy->listen_fd = -1; proxy->listen_fd = -1;
} }
if (-1 != proxy->downstream_fd) { if (-1 != proxy->downstream_fd) {
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(proxy->downstream_fd),
sock_try_close( proxy->downstream_fd ), SHOW_ERRNO("Failed to close() downstream fd %i",
SHOW_ERRNO( proxy->downstream_fd)
"Failed to close() downstream fd %i", proxy->downstream_fd
)
); );
proxy->downstream_fd = -1; proxy->downstream_fd = -1;
} }
if (-1 != proxy->upstream_fd) { if (-1 != proxy->upstream_fd) {
WARN_IF_NEGATIVE( WARN_IF_NEGATIVE(sock_try_close(proxy->upstream_fd),
sock_try_close( proxy->upstream_fd ), SHOW_ERRNO("Failed to close() upstream fd %i",
SHOW_ERRNO( proxy->upstream_fd)
"Failed to close() upstream fd %i", proxy->upstream_fd
)
); );
proxy->upstream_fd = -1; proxy->upstream_fd = -1;
} }
@@ -982,4 +1001,3 @@ int do_proxy( struct proxier* params )
return 0; return 0;
} }

View File

@@ -14,10 +14,10 @@
#endif #endif
/** UPSTREAM_TIMEOUT /** UPSTREAM_TIMEOUT
* How long ( in ms ) to allow for upstream to respond. If it takes longer * How long (in s) to allow for upstream to respond. If it takes longer
* than this, we will cancel the current request-response to them and resubmit * than this, we will cancel the current request-response to them and resubmit
*/ */
#define UPSTREAM_TIMEOUT 30 * 1000 #define UPSTREAM_TIMEOUT 30
struct proxier { struct proxier {
/** address/port to bind to */ /** address/port to bind to */
@@ -46,6 +46,9 @@ struct proxier {
/* This is the size we advertise to the downstream server */ /* This is the size we advertise to the downstream server */
uint64_t upstream_size; uint64_t upstream_size;
/* These are the transmission flags sent as part of the handshake */
uint32_t upstream_flags;
/* We transform the raw request header into here */ /* We transform the raw request header into here */
struct nbd_request req_hdr; struct nbd_request req_hdr;
@@ -69,6 +72,16 @@ struct proxier {
uint64_t req_count; uint64_t req_count;
int hello_sent; int hello_sent;
/*
* How long (in s) to allow for upstream to respond. If it takes longer
* than this, we will cancel the current request-response to them and
* resubmit
*
* Defaults to UPSTREAM_TIMEOUT but can be overridden in the environment.
*/
int upstream_timeout;
unsigned long int upstream_timeout_ms;
/** These are only used if we pass --cache on the command line */ /** These are only used if we pass --cache on the command line */
/* While the in-flight request has been munged by prefetch, these two are /* While the in-flight request has been munged by prefetch, these two are
@@ -82,16 +95,13 @@ struct proxier {
/** */ /** */
}; };
struct proxier* proxy_create( struct proxier *proxy_create(char *s_downstream_address,
char* s_downstream_address,
char *s_downstream_port, char *s_downstream_port,
char *s_upstream_address, char *s_upstream_address,
char *s_upstream_port, char *s_upstream_port,
char* s_upstream_bind, char *s_upstream_bind, char *s_cache_bytes);
char* s_cache_bytes);
int do_proxy(struct proxier *proxy); int do_proxy(struct proxier *proxy);
void proxy_cleanup(struct proxier *proxy); void proxy_cleanup(struct proxier *proxy);
void proxy_destroy(struct proxier *proxy); void proxy_destroy(struct proxier *proxy);
#endif #endif

View File

@@ -22,7 +22,9 @@ static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 };
/** Test whether AF_INET or AF_INET6 sockaddr is included in the given access /** Test whether AF_INET or AF_INET6 sockaddr is included in the given access
* control list, returning 1 if it is, and 0 if not. * control list, returning 1 if it is, and 0 if not.
*/ */
static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], union mysockaddr* test) static int is_included_in_acl(int list_length,
struct ip_and_mask (*list)[],
union mysockaddr *test)
{ {
NULLCHECK(test); NULLCHECK(test);
@@ -33,7 +35,8 @@ static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], uni
int testbits; int testbits;
unsigned char *raw_address1 = NULL, *raw_address2 = NULL; unsigned char *raw_address1 = NULL, *raw_address2 = NULL;
debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family, entry->ip.family); debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family,
entry->ip.family);
if (test->generic.sa_family != entry->ip.family) { if (test->generic.sa_family != entry->ip.family) {
continue; continue;
@@ -43,24 +46,24 @@ static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], uni
debug("it's an AF_INET"); debug("it's an AF_INET");
raw_address1 = (unsigned char *) &test->v4.sin_addr; raw_address1 = (unsigned char *) &test->v4.sin_addr;
raw_address2 = (unsigned char *) &entry->ip.v4.sin_addr; raw_address2 = (unsigned char *) &entry->ip.v4.sin_addr;
} } else if (test->generic.sa_family == AF_INET6) {
else if (test->generic.sa_family == AF_INET6) {
debug("it's an AF_INET6"); debug("it's an AF_INET6");
raw_address1 = (unsigned char *) &test->v6.sin6_addr; raw_address1 = (unsigned char *) &test->v6.sin6_addr;
raw_address2 = (unsigned char *) &entry->ip.v6.sin6_addr; raw_address2 = (unsigned char *) &entry->ip.v6.sin6_addr;
} } else {
else {
fatal("Can't check an ACL for this address type."); fatal("Can't check an ACL for this address type.");
} }
debug("testbits=%d", entry->mask); debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) { for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]); debug("testbits=%d, c1=%02x, c2=%02x", testbits,
raw_address1[0], raw_address2[0]);
if (testbits >= 8) { if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0]) { goto no_match; } if (raw_address1[0] != raw_address2[0]) {
goto no_match;
} }
else { } else {
if ((raw_address1[0] & testmasks[testbits % 8]) != if ((raw_address1[0] & testmasks[testbits % 8]) !=
(raw_address2[0] & testmasks[testbits % 8])) { (raw_address2[0] & testmasks[testbits % 8])) {
goto no_match; goto no_match;
@@ -86,8 +89,7 @@ int acl_includes( struct acl * acl, union mysockaddr * addr )
if (0 == acl->len) { if (0 == acl->len) {
return !(acl->default_deny); return !(acl->default_deny);
} } else {
else {
return is_included_in_acl(acl->len, acl->entries, addr); return is_included_in_acl(acl->len, acl->entries, addr);
} }
} }
@@ -105,4 +107,3 @@ void acl_destroy( struct acl * acl )
acl->entries = NULL; acl->entries = NULL;
free(acl); free(acl);
} }

View File

@@ -30,30 +30,41 @@ typedef bitfield_word_t * bitfield_p;
((_bytes + (BITFIELD_WORD_SIZE-1)) / BITFIELD_WORD_SIZE) ((_bytes + (BITFIELD_WORD_SIZE-1)) / BITFIELD_WORD_SIZE)
/** Return the bit value ''idx'' in array ''b'' */ /** Return the bit value ''idx'' in array ''b'' */
static inline int bit_get(bitfield_p b, uint64_t idx) { static inline int bit_get(bitfield_p b, uint64_t idx)
{
return (BIT_WORD(b, idx) >> (idx & (BITS_PER_WORD - 1))) & 1; return (BIT_WORD(b, idx) >> (idx & (BITS_PER_WORD - 1))) & 1;
} }
/** Return 1 if the bit at ''idx'' in array ''b'' is set */ /** Return 1 if the bit at ''idx'' in array ''b'' is set */
static inline int bit_is_set(bitfield_p b, uint64_t idx) { static inline int bit_is_set(bitfield_p b, uint64_t idx)
{
return bit_get(b, idx); return bit_get(b, idx);
} }
/** Return 1 if the bit at ''idx'' in array ''b'' is clear */ /** Return 1 if the bit at ''idx'' in array ''b'' is clear */
static inline int bit_is_clear(bitfield_p b, uint64_t idx) { static inline int bit_is_clear(bitfield_p b, uint64_t idx)
{
return !bit_get(b, idx); return !bit_get(b, idx);
} }
/** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */ /** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */
static inline int bit_has_value(bitfield_p b, uint64_t idx, int value) { static inline int bit_has_value(bitfield_p b, uint64_t idx, int value)
{
return bit_get(b, idx) == ! !value; return bit_get(b, idx) == ! !value;
} }
/** Sets the bit ''idx'' in array ''b'' */ /** Sets the bit ''idx'' in array ''b'' */
static inline void bit_set(bitfield_p b, uint64_t idx) { static inline void bit_set(bitfield_p b, uint64_t idx)
{
BIT_WORD(b, idx) |= BIT_MASK(idx); BIT_WORD(b, idx) |= BIT_MASK(idx);
} }
/** Clears the bit ''idx'' in array ''b'' */ /** Clears the bit ''idx'' in array ''b'' */
static inline void bit_clear(bitfield_p b, uint64_t idx) { static inline void bit_clear(bitfield_p b, uint64_t idx)
{
BIT_WORD(b, idx) &= ~BIT_MASK(idx); BIT_WORD(b, idx) &= ~BIT_MASK(idx);
} }
/** Sets ''len'' bits in array ''b'' starting at offset ''from'' */ /** Sets ''len'' bits in array ''b'' starting at offset ''from'' */
static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len) static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
{ {
@@ -72,8 +83,10 @@ static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
bit_set(b, from++); bit_set(b, from++);
} }
} }
/** Clears ''len'' bits in array ''b'' starting at offset ''from'' */ /** Clears ''len'' bits in array ''b'' starting at offset ''from'' */
static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len) static inline void bit_clear_range(bitfield_p b, uint64_t from,
uint64_t len)
{ {
for (; (from % BITS_PER_WORD) != 0 && len > 0; len--) { for (; (from % BITS_PER_WORD) != 0 && len > 0; len--) {
bit_clear(b, from++); bit_clear(b, from++);
@@ -96,7 +109,9 @@ static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len)
* bits that are the same as the first one specified. If ''run_is_set'' is * bits that are the same as the first one specified. If ''run_is_set'' is
* non-NULL, the value of that bit is placed into it. * non-NULL, the value of that bit is placed into it.
*/ */
static inline uint64_t bit_run_count(bitfield_p b, uint64_t from, uint64_t len, int *run_is_set) { static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
uint64_t len, int *run_is_set)
{
uint64_t count = 0; uint64_t count = 0;
int first_value = bit_get(b, from); int first_value = bit_get(b, from);
bitfield_word_t word_match = first_value ? -1 : 0; bitfield_word_t word_match = first_value ? -1 : 0;
@@ -185,8 +200,10 @@ static inline struct bitset *bitset_alloc( uint64_t size, int resolution )
// calculate a size to allocate that is a multiple of the size of the // calculate a size to allocate that is a multiple of the size of the
// bitfield word // bitfield word
size_t bitfield_size = size_t bitfield_size =
BIT_WORDS_FOR_SIZE((( size + resolution - 1 ) / resolution)) * sizeof( bitfield_word_t ); BIT_WORDS_FOR_SIZE(((size + resolution -
struct bitset *bitset = xmalloc(sizeof( struct bitset ) + ( bitfield_size / 8 ) ); 1) / resolution)) * sizeof(bitfield_word_t);
struct bitset *bitset =
xmalloc(sizeof(struct bitset) + (bitfield_size / 8));
bitset->size = size; bitset->size = size;
bitset->resolution = resolution; bitset->resolution = resolution;
@@ -224,12 +241,9 @@ static inline void bitset_free( struct bitset * set )
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset") FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
static inline void bitset_stream_enqueue( static inline void bitset_stream_enqueue(struct bitset *set,
struct bitset * set,
enum bitset_stream_events event, enum bitset_stream_events event,
uint64_t from, uint64_t from, uint64_t len)
uint64_t len
)
{ {
struct bitset_stream *stream = set->stream; struct bitset_stream *stream = set->stream;
@@ -254,10 +268,8 @@ static inline void bitset_stream_enqueue(
return; return;
} }
static inline void bitset_stream_dequeue( static inline void bitset_stream_dequeue(struct bitset *set,
struct bitset * set, struct bitset_stream_entry *out)
struct bitset_stream_entry * out
)
{ {
struct bitset_stream *stream = set->stream; struct bitset_stream *stream = set->stream;
struct bitset_stream_entry *dequeued; struct bitset_stream_entry *dequeued;
@@ -298,10 +310,9 @@ static inline size_t bitset_stream_size( struct bitset * set )
return size; return size;
} }
static inline uint64_t bitset_stream_queued_bytes( static inline uint64_t bitset_stream_queued_bytes(struct bitset *set,
struct bitset * set, enum bitset_stream_events
enum bitset_stream_events event event)
)
{ {
uint64_t total; uint64_t total;
@@ -331,10 +342,8 @@ static inline void bitset_disable_stream( struct bitset * set )
/** Set the bits in a bitset which correspond to the given bytes in the larger /** Set the bits in a bitset which correspond to the given bytes in the larger
* file. * file.
*/ */
static inline void bitset_set_range( static inline void bitset_set_range(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
@@ -357,10 +366,8 @@ static inline void bitset_set( struct bitset * set )
/** Clear the bits in a bitset which correspond to the given bytes in the /** Clear the bits in a bitset which correspond to the given bytes in the
* larger file. * larger file.
*/ */
static inline void bitset_clear_range( static inline void bitset_clear_range(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
@@ -383,12 +390,9 @@ static inline void bitset_clear( struct bitset * set )
/** As per bitset_run_count but also tells you whether the run it found was set /** As per bitset_run_count but also tells you whether the run it found was set
* or unset, atomically. * or unset, atomically.
*/ */
static inline uint64_t bitset_run_count_ex( static inline uint64_t bitset_run_count_ex(struct bitset *set,
struct bitset * set,
uint64_t from, uint64_t from,
uint64_t len, uint64_t len, int *run_is_set)
int* run_is_set
)
{ {
uint64_t run; uint64_t run;
@@ -401,7 +405,9 @@ static inline uint64_t bitset_run_count_ex(
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution; run =
bit_run_count(set->bits, first, bitlen,
run_is_set) * set->resolution;
run -= (from % set->resolution); run -= (from % set->resolution);
BITSET_UNLOCK; BITSET_UNLOCK;
@@ -411,10 +417,8 @@ static inline uint64_t bitset_run_count_ex(
/** Counts the number of contiguous bytes that are represented as a run in /** Counts the number of contiguous bytes that are represented as a run in
* the bit field. * the bit field.
*/ */
static inline uint64_t bitset_run_count( static inline uint64_t bitset_run_count(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
return bitset_run_count_ex(set, from, len, NULL); return bitset_run_count_ex(set, from, len, NULL);
} }
@@ -435,4 +439,3 @@ static inline int bitset_is_set_at( struct bitset * set, uint64_t at )
#endif #endif

View File

@@ -18,14 +18,16 @@
// When this signal is invoked, we call shutdown() on the client fd, which // When this signal is invoked, we call shutdown() on the client fd, which
// results in the thread being wound up // results in the thread being wound up
void client_killswitch_hit(int signal __attribute__ ((unused)), siginfo_t *info, void *ptr __attribute__ ((unused))) void client_killswitch_hit(int signal
__attribute__ ((unused)), siginfo_t * info,
void *ptr __attribute__ ((unused)))
{ {
int fd = info->si_value.sival_int; int fd = info->si_value.sival_int;
warn("Killswitch for fd %i activated, calling shutdown on socket", fd); warn("Killswitch for fd %i activated, calling shutdown on socket", fd);
FATAL_IF( FATAL_IF(-1 == shutdown(fd, SHUT_RDWR),
-1 == shutdown( fd, SHUT_RDWR ), SHOW_ERRNO
SHOW_ERRNO( "Failed to shutdown() the socket, killing the server" ) ("Failed to shutdown() the socket, killing the server")
); );
} }
@@ -53,8 +55,8 @@ struct client *client_create( struct server *serve, int socket )
c->stop_signal = self_pipe_create(); c->stop_signal = self_pipe_create();
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(timer_create
timer_create( CLOCK_MONOTONIC, &evp, &(c->killswitch) ), (CLOCK_MONOTONIC, &evp, &(c->killswitch)),
SHOW_ERRNO("Failed to create killswitch timer") SHOW_ERRNO("Failed to create killswitch timer")
); );
@@ -67,7 +69,8 @@ void client_signal_stop( struct client *c)
{ {
NULLCHECK(c); NULLCHECK(c);
debug("client %p: signal stop (%d, %d)", c,c->stop_signal->read_fd, c->stop_signal->write_fd ); debug("client %p: signal stop (%d, %d)", c, c->stop_signal->read_fd,
c->stop_signal->write_fd);
self_pipe_signal(c->stop_signal); self_pipe_signal(c->stop_signal);
} }
@@ -75,8 +78,7 @@ void client_destroy( struct client *client )
{ {
NULLCHECK(client); NULLCHECK(client);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(timer_delete(client->killswitch),
timer_delete( client->killswitch ),
SHOW_ERRNO("Couldn't delete killswitch") SHOW_ERRNO("Couldn't delete killswitch")
); );
@@ -119,7 +121,8 @@ void write_not_zeroes(struct client* client, uint64_t from, uint64_t len)
uint64_t run = bitset_run_count(map, from, len); uint64_t run = bitset_run_count(map, from, len);
debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len, run); debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len,
run);
if (run > len) { if (run > len) {
run = len; run = len;
@@ -164,8 +167,7 @@ void write_not_zeroes(struct client* client, uint64_t from, uint64_t len)
bitset_set_range(map, from, run); bitset_set_range(map, from, run);
len -= run; len -= run;
from += run; from += run;
} } else {
else {
char zerobuffer[block_allocation_resolution]; char zerobuffer[block_allocation_resolution];
/* not allocated, read in block_allocation_resoution */ /* not allocated, read in block_allocation_resoution */
while (run > 0) { while (run > 0) {
@@ -183,7 +185,8 @@ void write_not_zeroes(struct client* client, uint64_t from, uint64_t len)
*/ */
int all_zeros = (zerobuffer[0] == 0) && int all_zeros = (zerobuffer[0] == 0) &&
(0 == memcmp( zerobuffer, zerobuffer+1, blockrun-1 )); (0 ==
memcmp(zerobuffer, zerobuffer + 1, blockrun - 1));
if (!all_zeros) { if (!all_zeros) {
memcpy(client->mapped + from, zerobuffer, blockrun); memcpy(client->mapped + from, zerobuffer, blockrun);
@@ -217,7 +220,8 @@ int fd_read_request( int fd, struct nbd_request_raw *out_request)
/* Returns 1 if *request was filled with a valid request which we should /* Returns 1 if *request was filled with a valid request which we should
* try to honour. 0 otherwise. */ * try to honour. 0 otherwise. */
int client_read_request( struct client * client , struct nbd_request *out_request, int * disconnected ) int client_read_request(struct client *client,
struct nbd_request *out_request, int *disconnected)
{ {
NULLCHECK(client); NULLCHECK(client);
NULLCHECK(out_request); NULLCHECK(out_request);
@@ -228,11 +232,13 @@ int client_read_request( struct client * client , struct nbd_request *out_reques
*disconnected = 1; *disconnected = 1;
switch (errno) { switch (errno) {
case 0: case 0:
debug( "EOF while reading request" ); warn("EOF while reading request");
return 0; return 0;
case ECONNRESET: case ECONNRESET:
debug( "Connection reset while" warn("Connection reset while" " reading request");
" reading request" ); return 0;
case ETIMEDOUT:
warn("Connection timed out while" " reading request");
return 0; return 0;
default: default:
/* FIXME: I've seen this happen, but I /* FIXME: I've seen this happen, but I
@@ -242,9 +248,7 @@ int client_read_request( struct client * client , struct nbd_request *out_reques
* again. It should *probably* be an * again. It should *probably* be an
* error() call, but I want to be sure. * error() call, but I want to be sure.
* */ * */
fatal("Error reading request: %d, %s", fatal("Error reading request: %d, %s", errno, strerror(errno));
errno,
strerror( errno ));
} }
} }
@@ -289,7 +293,8 @@ int fd_write_reply( int fd, uint64_t handle, int error )
* Returns 1; we don't check for errors on the write. * Returns 1; we don't check for errors on the write.
* TODO: Check for errors on the write. * TODO: Check for errors on the write.
*/ */
int client_write_reply( struct client * client, struct nbd_request *request, int error ) int client_write_reply(struct client *client, struct nbd_request *request,
int error)
{ {
return fd_write_reply(client->socket, request->handle.w, error); return fd_write_reply(client->socket, request->handle.w, error);
} }
@@ -303,14 +308,17 @@ void client_write_init( struct client * client, uint64_t size )
memcpy(init.passwd, INIT_PASSWD, sizeof(init.passwd)); memcpy(init.passwd, INIT_PASSWD, sizeof(init.passwd));
init.magic = INIT_MAGIC; init.magic = INIT_MAGIC;
init.size = size; init.size = size;
memset( init.reserved, 0, 128 ); /* As more features are implemented, this is the place to advertise
* them.
*/
init.flags = FLAG_HAS_FLAGS | FLAG_SEND_FLUSH | FLAG_SEND_FUA;
memset(init.reserved, 0, 124);
nbd_h2r_init(&init, &init_raw); nbd_h2r_init(&init, &init_raw);
ERROR_IF_NEGATIVE( ERROR_IF_NEGATIVE(writeloop
writeloop(client->socket, &init_raw, sizeof(init_raw)), (client->socket, &init_raw, sizeof(init_raw)),
"Couldn't send hello" "Couldn't send hello");
);
} }
@@ -330,23 +338,15 @@ void client_flush( struct client * client, size_t len )
size_t spliced = 0; size_t spliced = 0;
while (spliced < len) { while (spliced < len) {
ssize_t received = splice( ssize_t received = splice(client->socket, NULL,
client->socket, NULL,
pipes[1], NULL, pipes[1], NULL,
len - spliced, flags); len - spliced, flags);
FATAL_IF_NEGATIVE( received, FATAL_IF_NEGATIVE(received, "splice error: %s", strerror(errno));
"splice error: %s",
strerror(errno));
ssize_t junked = 0; ssize_t junked = 0;
while (junked < received) { while (junked < received) {
ssize_t junk; ssize_t junk;
junk = splice( junk = splice(pipes[0], NULL, devnull, NULL, received, flags);
pipes[0], NULL, FATAL_IF_NEGATIVE(junk, "splice error: %s", strerror(errno));
devnull, NULL,
received, flags );
FATAL_IF_NEGATIVE( junk,
"splice error: %s",
strerror(errno));
junked += junk; junked += junk;
} }
spliced += received; spliced += received;
@@ -378,27 +378,26 @@ int client_request_needs_reply( struct client * client,
return 0; return 0;
} }
debug( debug("request type=%" PRIu16 ", flags=%" PRIu16 ", from=%" PRIu64
"request type=%"PRIu32", from=%"PRIu64", len=%"PRIu32", handle=0x%08X", ", len=%" PRIu32 ", handle=0x%08X", request.type, request.flags,
request.type, request.from, request.len, request.handle request.from, request.len, request.handle);
);
/* check it's not out of range */ /* check it's not out of range. NBD protocol requires ENOSPC to be
* returned in this instance
*/
if (request.from + request.len > client->serve->size) { if (request.from + request.len > client->serve->size) {
warn("write request %" PRIu64 "+%" PRIu32 " out of range", warn("write request %" PRIu64 "+%" PRIu32 " out of range",
request.from, request.len request.from, request.len);
);
if (request.type == REQUEST_WRITE) { if (request.type == REQUEST_WRITE) {
client_flush(client, request.len); client_flush(client, request.len);
} }
client_write_reply( client, &request, EPERM ); /* TODO: Change to ERANGE ? */ client_write_reply(client, &request, ENOSPC);
client->disconnect = 0; client->disconnect = 0;
return 0; return 0;
} }
switch (request.type) switch (request.type) {
{
case REQUEST_READ: case REQUEST_READ:
break; break;
case REQUEST_WRITE: case REQUEST_WRITE:
@@ -407,15 +406,22 @@ int client_request_needs_reply( struct client * client,
debug("request disconnect"); debug("request disconnect");
client->disconnect = 1; client->disconnect = 1;
return 0; return 0;
case REQUEST_FLUSH:
break;
default: default:
fatal("Unknown request 0x%08X", request.type); /* NBD prototcol says servers SHOULD return EINVAL to unknown
* commands */
warn("Unknown request 0x%08X", request.type);
client_write_reply(client, &request, EINVAL);
client->disconnect = 0;
return 0;
} }
return 1; return 1;
} }
void client_reply_to_read( struct client* client, struct nbd_request request ) void client_reply_to_read(struct client *client,
struct nbd_request request)
{ {
off64_t offset; off64_t offset;
@@ -428,62 +434,74 @@ void client_reply_to_read( struct client* client, struct nbd_request request )
/* If we get cut off partway through this sendfile, we don't /* If we get cut off partway through this sendfile, we don't
* want to kill the server. This should be an error. * want to kill the server. This should be an error.
*/ */
ERROR_IF_NEGATIVE( ERROR_IF_NEGATIVE(sendfileloop(client->socket,
sendfileloop(
client->socket,
client->fileno, client->fileno,
&offset, &offset,
request.len), request.len),
"sendfile failed from=%ld, len=%d", "sendfile failed from=%ld, len=%d",
offset, offset, request.len);
request.len);
sock_set_tcp_cork(client->socket, 0); sock_set_tcp_cork(client->socket, 0);
} }
void client_reply_to_write( struct client* client, struct nbd_request request ) void client_reply_to_write(struct client *client,
struct nbd_request request)
{ {
debug("request write from=%"PRIu64", len=%"PRIu32", handle=0x%08X", request.from, request.len, request.handle); debug("request write from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X",
request.from, request.len, request.handle);
if (client->serve->allocation_map_built) { if (client->serve->allocation_map_built) {
write_not_zeroes(client, request.from, request.len); write_not_zeroes(client, request.from, request.len);
} } else {
else {
debug("No allocation map, writing directly."); debug("No allocation map, writing directly.");
/* If we get cut off partway through reading this data: /* If we get cut off partway through reading this data:
* */ * */
ERROR_IF_NEGATIVE( ERROR_IF_NEGATIVE(readloop(client->socket,
readloop( client->socket,
client->mapped + request.from, client->mapped + request.from,
request.len), request.len),
"reading write data failed from=%ld, len=%d", "reading write data failed from=%ld, len=%d",
request.from, request.from, request.len);
request.len
);
/* the allocation_map is shared between client threads, and may be /* the allocation_map is shared between client threads, and may be
* being built. We need to reflect the write in it, as it may be in * being built. We need to reflect the write in it, as it may be in
* a position the builder has already gone over. * a position the builder has already gone over.
*/ */
bitset_set_range(client->serve->allocation_map, request.from, request.len); bitset_set_range(client->serve->allocation_map, request.from,
request.len);
} }
if (1) /* not sure whether this is necessary... */ // Only flush if FUA is set -- overridden for now to force flush after each
{ // write.
/* multiple of 4K page size */ // if (request.flags & CMD_FLAG_FUA) {
uint64_t from_rounded = request.from & (!0xfff); if (1) {
/* multiple of page size */
uint64_t from_rounded =
request.from & (~(sysconf(_SC_PAGE_SIZE) - 1));
uint64_t len_rounded = request.len + (request.from - from_rounded); uint64_t len_rounded = request.len + (request.from - from_rounded);
debug("Calling msync from=%" PRIu64 ", len=%" PRIu64 "",
from_rounded, len_rounded);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(msync(client->mapped + from_rounded,
msync( client->mapped + from_rounded,
len_rounded, len_rounded,
MS_SYNC | MS_INVALIDATE), MS_SYNC | MS_INVALIDATE),
"msync failed %ld %ld", request.from, request.len "msync failed %ld %ld", request.from,
); request.len);
} }
client_write_reply(client, &request, 0); client_write_reply(client, &request, 0);
} }
void client_reply_to_flush(struct client *client,
struct nbd_request request)
{
debug("request flush from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X",
request.from, request.len, request.handle);
ERROR_IF_NEGATIVE(msync
(client->mapped, client->mapped_size,
MS_SYNC | MS_INVALIDATE), "flush failed");
client_write_reply(client, &request, 0);
}
void client_reply(struct client *client, struct nbd_request request) void client_reply(struct client *client, struct nbd_request request)
{ {
@@ -494,6 +512,9 @@ void client_reply( struct client* client, struct nbd_request request )
case REQUEST_WRITE: case REQUEST_WRITE:
client_reply_to_write(client, request); client_reply_to_write(client, request);
break; break;
case REQUEST_FLUSH:
client_reply_to_flush(client, request);
break;
} }
} }
@@ -514,8 +535,7 @@ void client_arm_killswitch( struct client* client )
debug("Arming killswitch"); debug("Arming killswitch");
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL),
timer_settime( client->killswitch, 0, &its, NULL ),
SHOW_ERRNO("Failed to arm killswitch") SHOW_ERRNO("Failed to arm killswitch")
); );
@@ -535,8 +555,7 @@ void client_disarm_killswitch( struct client* client )
debug("Disarming killswitch"); debug("Disarming killswitch");
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL),
timer_settime( client->killswitch, 0, &its, NULL ),
SHOW_ERRNO("Failed to disarm killswitch") SHOW_ERRNO("Failed to disarm killswitch")
); );
@@ -572,8 +591,9 @@ int client_serve_request(struct client* client)
if (fd_count == 0) { if (fd_count == 0) {
/* This "can't ever happen" */ /* This "can't ever happen" */
fatal("No FDs selected, and no timeout!"); fatal("No FDs selected, and no timeout!");
} else if (fd_count < 0) {
fatal("Select failed");
} }
else if ( fd_count < 0 ) { fatal( "Select failed" ); }
if (self_pipe_fd_isset(client->stop_signal, &rfds)) { if (self_pipe_fd_isset(client->stop_signal, &rfds)) {
debug("Client received stop signal."); debug("Client received stop signal.");
@@ -651,13 +671,14 @@ void client_cleanup(struct client* client,
} }
if (client->fileno) { if (client->fileno) {
FATAL_IF_NEGATIVE(close(client->fileno), FATAL_IF_NEGATIVE(close(client->fileno),
"Error closing file %d", "Error closing file %d", client->fileno);
client->fileno );
debug("Closed client file fd %d", client->fileno); debug("Closed client file fd %d", client->fileno);
client->fileno = -1; client->fileno = -1;
} }
if ( server_acl_locked( client->serve ) ) { server_unlock_acl( client->serve ); } if (server_acl_locked(client->serve)) {
server_unlock_acl(client->serve);
}
} }
@@ -668,19 +689,18 @@ void* client_serve(void* client_uncast)
error_set_handler((cleanup_handler *) client_cleanup, client); error_set_handler((cleanup_handler *) client_cleanup, client);
info("client: mmaping file"); info("client: mmaping file");
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(open_and_mmap(client->serve->filename,
open_and_mmap(
client->serve->filename,
&client->fileno, &client->fileno,
NULL, &client->mapped_size,
(void**) &client->mapped (void **) &client->mapped),
), "Couldn't open/mmap file %s: %s",
"Couldn't open/mmap file %s: %s", client->serve->filename, strerror( errno ) client->serve->filename, strerror(errno)
); );
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(madvise
madvise( client->mapped, client->serve->size, MADV_RANDOM ), (client->mapped, client->serve->size, MADV_RANDOM),
SHOW_ERRNO( "Failed to madvise() %s", client->serve->filename ) SHOW_ERRNO("Failed to madvise() %s",
client->serve->filename)
); );
debug("Opened client file fd %d", client->fileno); debug("Opened client file fd %d", client->fileno);
@@ -688,8 +708,7 @@ void* client_serve(void* client_uncast)
client_send_hello(client); client_send_hello(client);
debug("client: serving requests"); debug("client: serving requests");
while (client_serve_request(client) == 0) while (client_serve_request(client) == 0);
;
debug("client: stopped serving requests"); debug("client: stopped serving requests");
client->stopped = 1; client->stopped = 1;
@@ -698,10 +717,10 @@ void* client_serve(void* client_uncast)
server_control_arrived(client->serve); server_control_arrived(client->serve);
} }
debug("Cleaning client %p up normally in thread %p", client, pthread_self()); debug("Cleaning client %p up normally in thread %p", client,
pthread_self());
client_cleanup(client, 0); client_cleanup(client, 0);
debug("Client thread done"); debug("Client thread done");
return NULL; return NULL;
} }

View File

@@ -3,6 +3,7 @@
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <inttypes.h>
/** CLIENT_HANDLER_TIMEOUT /** CLIENT_HANDLER_TIMEOUT
* This is the length of time (in seconds) any request can be outstanding for. * This is the length of time (in seconds) any request can be outstanding for.
@@ -31,6 +32,8 @@ struct client {
int fileno; int fileno;
char *mapped; char *mapped;
uint64_t mapped_size;
struct self_pipe *stop_signal; struct self_pipe *stop_signal;
struct server *serve; /* FIXME: remove above duplication */ struct server *serve; /* FIXME: remove above duplication */
@@ -53,4 +56,3 @@ void client_destroy( struct client * client );
void client_signal_stop(struct client *client); void client_signal_stop(struct client *client);
#endif #endif

View File

@@ -44,9 +44,7 @@
#include <unistd.h> #include <unistd.h>
struct control * control_create( struct control *control_create(struct flexnbd *flexnbd, const char *csn)
struct flexnbd * flexnbd,
const char * csn)
{ {
struct control *control = xmalloc(sizeof(struct control)); struct control *control = xmalloc(sizeof(struct control));
@@ -80,8 +78,15 @@ void control_destroy( struct control * control )
free(control); free(control);
} }
struct control_client * control_client_create( void control_wait_for_close(struct control *control)
struct flexnbd * flexnbd, {
NULLCHECK(control);
while (!fd_is_closed(control->control_fd)) {
usleep(10000);
}
}
struct control_client *control_client_create(struct flexnbd *flexnbd,
int client_fd, int client_fd,
struct mbox *state_mbox) struct mbox *state_mbox)
{ {
@@ -112,8 +117,7 @@ void control_handle_client( struct control * control, int client_fd )
NULLCHECK(control); NULLCHECK(control);
NULLCHECK(control->flexnbd); NULLCHECK(control->flexnbd);
struct control_client *control_client = struct control_client *control_client =
control_client_create( control_client_create(control->flexnbd,
control->flexnbd,
client_fd, client_fd,
control->mirror_state_mbox); control->mirror_state_mbox);
@@ -132,7 +136,8 @@ void control_accept_client( struct control * control )
union mysockaddr client_address; union mysockaddr client_address;
socklen_t addrlen = sizeof(union mysockaddr); socklen_t addrlen = sizeof(union mysockaddr);
client_fd = accept( control->control_fd, &client_address.generic, &addrlen ); client_fd =
accept(control->control_fd, &client_address.generic, &addrlen);
FATAL_IF(-1 == client_fd, "control accept failed"); FATAL_IF(-1 == client_fd, "control accept failed");
control_handle_client(control, client_fd); control_handle_client(control, client_fd);
@@ -178,25 +183,23 @@ int open_control_socket( const char * socket_name )
} }
control_fd = socket(AF_UNIX, SOCK_STREAM, 0); control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
FATAL_IF_NEGATIVE(control_fd , FATAL_IF_NEGATIVE(control_fd, "Couldn't create control socket");
"Couldn't create control socket");
memset(&bind_address, 0, sizeof(struct sockaddr_un)); memset(&bind_address, 0, sizeof(struct sockaddr_un));
bind_address.sun_family = AF_UNIX; bind_address.sun_family = AF_UNIX;
strncpy(bind_address.sun_path, socket_name, sizeof(bind_address.sun_path)-1); strncpy(bind_address.sun_path, socket_name,
sizeof(bind_address.sun_path) - 1);
//unlink(socket_name); /* ignore failure */ //unlink(socket_name); /* ignore failure */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(bind
bind(control_fd , &bind_address, sizeof(bind_address)), (control_fd, &bind_address, sizeof(bind_address)),
"Couldn't bind control socket to %s: %s", "Couldn't bind control socket to %s: %s",
socket_name, strerror(errno) socket_name, strerror(errno)
); );
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(listen(control_fd, 5),
listen(control_fd , 5), "Couldn't listen on control socket");
"Couldn't listen on control socket"
);
return control_fd; return control_fd;
} }
@@ -229,8 +232,7 @@ void control_serve( struct control * control )
} }
void control_cleanup( void control_cleanup(struct control *control,
struct control * control,
int fatal __attribute__ ((unused))) int fatal __attribute__ ((unused)))
{ {
NULLCHECK(control); NULLCHECK(control);
@@ -256,7 +258,8 @@ void * control_runner( void * control_uncast )
#define write_socket(msg) write(client_fd, (msg "\n"), strlen((msg))+1) #define write_socket(msg) write(client_fd, (msg "\n"), strlen((msg))+1)
void control_write_mirror_response( enum mirror_state mirror_state, int client_fd ) void control_write_mirror_response(enum mirror_state mirror_state,
int client_fd)
{ {
switch (mirror_state) { switch (mirror_state) {
case MS_INIT: case MS_INIT:
@@ -286,12 +289,12 @@ void control_write_mirror_response( enum mirror_state mirror_state, int client_f
fatal("Unhandled mirror state: %d", mirror_state); fatal("Unhandled mirror state: %d", mirror_state);
} }
} }
#undef write_socket #undef write_socket
/* Call this in the thread where you want to receive the mirror state */ /* Call this in the thread where you want to receive the mirror state */
enum mirror_state control_client_mirror_wait( enum mirror_state control_client_mirror_wait(struct control_client *client)
struct control_client* client)
{ {
NULLCHECK(client); NULLCHECK(client);
NULLCHECK(client->mirror_state_mbox); NULLCHECK(client->mirror_state_mbox);
@@ -345,14 +348,11 @@ int control_mirror(struct control_client* client, int linesc, char** lines)
if (linesc > 2) { if (linesc > 2) {
if (strcmp("exit", lines[2]) == 0) { if (strcmp("exit", lines[2]) == 0) {
action_at_finish = ACTION_EXIT; action_at_finish = ACTION_EXIT;
} } else if (strcmp("unlink", lines[2]) == 0) {
else if (strcmp( "unlink", lines[2]) == 0 ) {
action_at_finish = ACTION_UNLINK; action_at_finish = ACTION_UNLINK;
} } else if (strcmp("nothing", lines[2]) == 0) {
else if (strcmp("nothing", lines[2]) == 0) {
action_at_finish = ACTION_NOTHING; action_at_finish = ACTION_NOTHING;
} } else {
else {
write_socket("1: action must be 'exit' or 'nothing'"); write_socket("1: action must be 'exit' or 'nothing'");
return -1; return -1;
} }
@@ -389,13 +389,13 @@ int control_mirror(struct control_client* client, int linesc, char** lines)
server_lock_start_mirror(serve); server_lock_start_mirror(serve);
{ {
if (server_mirror_can_start(serve)) { if (server_mirror_can_start(serve)) {
serve->mirror_super = mirror_super_create( serve->mirror_super = mirror_super_create(serve->filename,
serve->filename,
connect_to, connect_to,
connect_from, connect_from,
max_Bps, max_Bps,
action_at_finish, action_at_finish,
client->mirror_state_mbox ); client->
mirror_state_mbox);
serve->mirror = serve->mirror_super->mirror; serve->mirror = serve->mirror_super->mirror;
server_prevent_mirror_start(serve); server_prevent_mirror_start(serve);
} else { } else {
@@ -415,18 +415,14 @@ int control_mirror(struct control_client* client, int linesc, char** lines)
* sighandler can block the serve thread * sighandler can block the serve thread
*/ */
if (serve->mirror_super) { if (serve->mirror_super) {
FATAL_IF( 0 != pthread_create( FATAL_IF(0 != pthread_create(&serve->mirror_super->thread,
&serve->mirror_super->thread,
NULL, NULL,
mirror_super_runner, mirror_super_runner,
serve serve),
), "Failed to create mirror thread");
"Failed to create mirror thread"
);
debug("Control thread mirror super waiting"); debug("Control thread mirror super waiting");
enum mirror_state state = enum mirror_state state = control_client_mirror_wait(client);
control_client_mirror_wait( client );
debug("Control thread writing response"); debug("Control thread writing response");
control_write_mirror_response(state, client->socket); control_write_mirror_response(state, client->socket);
} }
@@ -436,7 +432,8 @@ int control_mirror(struct control_client* client, int linesc, char** lines)
return 0; return 0;
} }
int control_mirror_max_bps( struct control_client* client, int linesc, char** lines ) int control_mirror_max_bps(struct control_client *client, int linesc,
char **lines)
{ {
NULLCHECK(client); NULLCHECK(client);
NULLCHECK(client->flexnbd); NULLCHECK(client->flexnbd);
@@ -489,8 +486,7 @@ int control_acl(struct control_client* client, int linesc, char** lines)
strlen(lines[new_acl->len])); strlen(lines[new_acl->len]));
write(client->socket, "\n", 1); write(client->socket, "\n", 1);
acl_destroy(new_acl); acl_destroy(new_acl);
} } else {
else {
flexnbd_replace_acl(flexnbd, new_acl); flexnbd_replace_acl(flexnbd, new_acl);
info("ACL set"); info("ACL set");
write(client->socket, "0: updated\n", 11); write(client->socket, "0: updated\n", 11);
@@ -500,8 +496,7 @@ int control_acl(struct control_client* client, int linesc, char** lines)
} }
int control_break( int control_break(struct control_client *client,
struct control_client* client,
int linesc __attribute__ ((unused)), int linesc __attribute__ ((unused)),
char **lines __attribute__ ((unused)) char **lines __attribute__ ((unused))
) )
@@ -524,13 +519,10 @@ int control_break(
if (server_is_closed(serve)) { if (server_is_closed(serve)) {
info("Mirror completed while canceling"); info("Mirror completed while canceling");
write( client->socket, write(client->socket, "1: mirror completed\n", 20);
"1: mirror completed\n", 20 ); } else {
}
else {
info("Mirror successfully stopped."); info("Mirror successfully stopped.");
write( client->socket, write(client->socket, "0: mirror stopped\n", 18);
"0: mirror stopped\n", 18 );
result = 1; result = 1;
} }
@@ -546,8 +538,7 @@ int control_break(
/** FIXME: add some useful statistics */ /** FIXME: add some useful statistics */
int control_status( int control_status(struct control_client *client,
struct control_client* client,
int linesc __attribute__ ((unused)), int linesc __attribute__ ((unused)),
char **lines __attribute__ ((unused)) char **lines __attribute__ ((unused))
) )
@@ -566,10 +557,14 @@ int control_status(
void control_client_cleanup(struct control_client *client, void control_client_cleanup(struct control_client *client,
int fatal __attribute__ ((unused))) int fatal __attribute__ ((unused)))
{ {
if (client->socket) { close(client->socket); } if (client->socket) {
close(client->socket);
}
/* This is wrongness */ /* This is wrongness */
if ( server_acl_locked( client->flexnbd->serve ) ) { server_unlock_acl( client->flexnbd->serve ); } if (server_acl_locked(client->flexnbd->serve)) {
server_unlock_acl(client->flexnbd->serve);
}
control_client_destroy(client); control_client_destroy(client);
} }
@@ -584,30 +579,25 @@ void control_respond(struct control_client * client)
int i, linesc; int i, linesc;
linesc = read_lines_until_blankline(client->socket, 256, &lines); linesc = read_lines_until_blankline(client->socket, 256, &lines);
if (linesc < 1) if (linesc < 1) {
{
write(client->socket, "9: missing command\n", 19); write(client->socket, "9: missing command\n", 19);
/* ignore failure */ /* ignore failure */
} } else if (strcmp(lines[0], "acl") == 0) {
else if (strcmp(lines[0], "acl") == 0) {
info("acl command received"); info("acl command received");
if (control_acl(client, linesc - 1, lines + 1) < 0) { if (control_acl(client, linesc - 1, lines + 1) < 0) {
debug("acl command failed"); debug("acl command failed");
} }
} } else if (strcmp(lines[0], "mirror") == 0) {
else if (strcmp(lines[0], "mirror") == 0) {
info("mirror command received"); info("mirror command received");
if (control_mirror(client, linesc - 1, lines + 1) < 0) { if (control_mirror(client, linesc - 1, lines + 1) < 0) {
debug("mirror command failed"); debug("mirror command failed");
} }
} } else if (strcmp(lines[0], "break") == 0) {
else if (strcmp(lines[0], "break") == 0) {
info("break command received"); info("break command received");
if (control_break(client, linesc - 1, lines + 1) < 0) { if (control_break(client, linesc - 1, lines + 1) < 0) {
debug("break command failed"); debug("break command failed");
} }
} } else if (strcmp(lines[0], "status") == 0) {
else if (strcmp(lines[0], "status") == 0) {
info("status command received"); info("status command received");
if (control_status(client, linesc - 1, lines + 1) < 0) { if (control_status(client, linesc - 1, lines + 1) < 0) {
debug("status command failed"); debug("status command failed");
@@ -617,8 +607,7 @@ void control_respond(struct control_client * client)
if (control_mirror_max_bps(client, linesc - 1, lines + 1) < 0) { if (control_mirror_max_bps(client, linesc - 1, lines + 1) < 0) {
debug("mirror_max_bps command failed"); debug("mirror_max_bps command failed");
} }
} } else {
else {
write(client->socket, "10: unknown command\n", 23); write(client->socket, "10: unknown command\n", 23);
} }
@@ -630,4 +619,3 @@ void control_respond(struct control_client * client)
control_client_cleanup(client, 0); control_client_cleanup(client, 0);
debug("control command handled"); debug("control command handled");
} }

View File

@@ -44,16 +44,16 @@ struct control_client{
struct mbox *mirror_state_mbox; struct mbox *mirror_state_mbox;
}; };
struct control * control_create( struct control *control_create(struct flexnbd *,
struct flexnbd *,
const char *control_socket_name); const char *control_socket_name);
void control_signal_close(struct control *); void control_signal_close(struct control *);
void control_wait_for_close(struct control *control);
void control_destroy(struct control *); void control_destroy(struct control *);
void *control_runner(void *); void *control_runner(void *);
void accept_control_connection(struct server* params, int client_fd, union mysockaddr* client_address); void accept_control_connection(struct server *params, int client_fd,
union mysockaddr *client_address);
void serve_open_control_socket(struct server *params); void serve_open_control_socket(struct server *params);
#endif #endif

View File

@@ -61,16 +61,13 @@ int flexnbd_build_signal_fd(void)
} }
void flexnbd_create_shared( void flexnbd_create_shared(struct flexnbd *flexnbd,
struct flexnbd * flexnbd,
const char *s_ctrl_sock) const char *s_ctrl_sock)
{ {
NULLCHECK(flexnbd); NULLCHECK(flexnbd);
if (s_ctrl_sock) { if (s_ctrl_sock) {
flexnbd->control = flexnbd->control = control_create(flexnbd, s_ctrl_sock);
control_create( flexnbd, s_ctrl_sock ); } else {
}
else {
flexnbd->control = NULL; flexnbd->control = NULL;
} }
@@ -78,8 +75,7 @@ void flexnbd_create_shared(
} }
struct flexnbd * flexnbd_create_serving( struct flexnbd *flexnbd_create_serving(char *s_ip_address,
char* s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
char *s_ctrl_sock, char *s_ctrl_sock,
@@ -90,17 +86,14 @@ struct flexnbd * flexnbd_create_serving(
int use_killswitch) int use_killswitch)
{ {
struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd)); struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd));
flexnbd->serve = server_create( flexnbd->serve = server_create(flexnbd,
flexnbd,
s_ip_address, s_ip_address,
s_port, s_port,
s_file, s_file,
default_deny, default_deny,
acl_entries, acl_entries,
s_acl_entries, s_acl_entries,
max_nbd_clients, max_nbd_clients, use_killswitch, 1);
use_killswitch,
1);
flexnbd_create_shared(flexnbd, s_ctrl_sock); flexnbd_create_shared(flexnbd, s_ctrl_sock);
// Beats installing one handler per client instance // Beats installing one handler per client instance
@@ -110,17 +103,14 @@ struct flexnbd * flexnbd_create_serving(
.sa_flags = SA_RESTART | SA_SIGINFO .sa_flags = SA_RESTART | SA_SIGINFO
}; };
FATAL_UNLESS( FATAL_UNLESS(0 == sigaction(CLIENT_KILLSWITCH_SIGNAL, &act, NULL),
0 == sigaction( CLIENT_KILLSWITCH_SIGNAL, &act, NULL ), "Installing client killswitch signal failed");
"Installing client killswitch signal failed"
);
} }
return flexnbd; return flexnbd;
} }
struct flexnbd * flexnbd_create_listening( struct flexnbd *flexnbd_create_listening(char *s_ip_address,
char* s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
char *s_ctrl_sock, char *s_ctrl_sock,
@@ -129,15 +119,12 @@ struct flexnbd * flexnbd_create_listening(
char **s_acl_entries) char **s_acl_entries)
{ {
struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd)); struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd));
flexnbd->serve = server_create( flexnbd->serve = server_create(flexnbd,
flexnbd,
s_ip_address, s_ip_address,
s_port, s_port,
s_file, s_file,
default_deny, default_deny,
acl_entries, acl_entries, s_acl_entries, 1, 0, 0);
s_acl_entries,
1, 0, 0);
flexnbd_create_shared(flexnbd, s_ctrl_sock); flexnbd_create_shared(flexnbd, s_ctrl_sock);
// listen can't use killswitch, as mirror may pause on sending things // listen can't use killswitch, as mirror may pause on sending things
@@ -153,8 +140,7 @@ void flexnbd_spawn_control(struct flexnbd * flexnbd )
pthread_t *control_thread = &flexnbd->control->thread; pthread_t *control_thread = &flexnbd->control->thread;
FATAL_UNLESS( 0 == pthread_create( FATAL_UNLESS(0 == pthread_create(control_thread,
control_thread,
NULL, NULL,
control_runner, control_runner,
flexnbd->control), flexnbd->control),
@@ -233,9 +219,7 @@ void make_writable( const char * filename )
{ {
NULLCHECK(filename); NULLCHECK(filename);
FATAL_IF_NEGATIVE(chmod(filename, S_IWUSR), FATAL_IF_NEGATIVE(chmod(filename, S_IWUSR),
"Couldn't chmod %s: %s", "Couldn't chmod %s: %s", filename, strerror(errno));
filename,
strerror( errno ) );
} }
@@ -262,4 +246,3 @@ int flexnbd_serve( struct flexnbd * flexnbd )
return success; return success;
} }

View File

@@ -29,8 +29,7 @@ struct flexnbd {
struct flexnbd *flexnbd_create(void); struct flexnbd *flexnbd_create(void);
struct flexnbd * flexnbd_create_serving( struct flexnbd *flexnbd_create_serving(char *s_ip_address,
char* s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
char *s_ctrl_sock, char *s_ctrl_sock,
@@ -40,8 +39,7 @@ struct flexnbd * flexnbd_create_serving(
int max_nbd_clients, int max_nbd_clients,
int use_killswitch); int use_killswitch);
struct flexnbd * flexnbd_create_listening( struct flexnbd *flexnbd_create_listening(char *s_ip_address,
char* s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
char *s_ctrl_sock, char *s_ctrl_sock,
@@ -63,4 +61,3 @@ struct server * flexnbd_server( struct flexnbd * flexnbd );
void flexnbd_replace_acl(struct flexnbd *flexnbd, struct acl *acl); void flexnbd_replace_acl(struct flexnbd *flexnbd, struct acl *acl);
struct status *flexnbd_status_create(struct flexnbd *flexnbd); struct status *flexnbd_status_create(struct flexnbd *flexnbd);
#endif #endif

View File

@@ -22,15 +22,14 @@ void flexthread_mutex_destroy( struct flexthread_mutex * ftm )
if (flexthread_mutex_held(ftm)) { if (flexthread_mutex_held(ftm)) {
flexthread_mutex_unlock(ftm); flexthread_mutex_unlock(ftm);
} } else if ((pthread_t) NULL != ftm->holder) {
else if ( (pthread_t)NULL != ftm->holder ) {
/* This "should never happen": if we can try to destroy /* This "should never happen": if we can try to destroy
* a mutex currently held by another thread, there's a * a mutex currently held by another thread, there's a
* logic bug somewhere. I know the test here is racy, * logic bug somewhere. I know the test here is racy,
* but there's not a lot we can do about it at this * but there's not a lot we can do about it at this
* point. * point.
*/ */
fatal( "Attempted to destroy a flexthread_mutex"\ fatal("Attempted to destroy a flexthread_mutex"
" held by another thread!"); " held by another thread!");
} }
@@ -72,4 +71,3 @@ int flexthread_mutex_held( struct flexthread_mutex * ftm )
NULLCHECK(ftm); NULLCHECK(ftm);
return pthread_self() == ftm->holder; return pthread_self() == ftm->holder;
} }

View File

@@ -90,8 +90,7 @@ struct mirror_ctrl {
}; };
struct mirror * mirror_alloc( struct mirror *mirror_alloc(union mysockaddr *connect_to,
union mysockaddr * connect_to,
union mysockaddr *connect_from, union mysockaddr *connect_from,
uint64_t max_Bps, uint64_t max_Bps,
enum mirror_finish_action action_at_finish, enum mirror_finish_action action_at_finish,
@@ -144,19 +143,13 @@ void mirror_init( struct mirror * mirror, const char * filename )
NULLCHECK(mirror); NULLCHECK(mirror);
NULLCHECK(filename); NULLCHECK(filename);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(open_and_mmap(filename,
open_and_mmap(
filename,
&map_fd, &map_fd,
&size, &size,
(void**) &mirror->mapped (void **) &mirror->mapped),
), "Failed to open and mmap %s", filename);
"Failed to open and mmap %s",
filename
);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(madvise(mirror->mapped, size, MADV_SEQUENTIAL),
madvise( mirror->mapped, size, MADV_SEQUENTIAL ),
SHOW_ERRNO("Failed to madvise() %s", filename) SHOW_ERRNO("Failed to madvise() %s", filename)
); );
} }
@@ -176,8 +169,7 @@ void mirror_reset( struct mirror * mirror )
} }
struct mirror * mirror_create( struct mirror *mirror_create(const char *filename,
const char * filename,
union mysockaddr *connect_to, union mysockaddr *connect_to,
union mysockaddr *connect_from, union mysockaddr *connect_from,
uint64_t max_Bps, uint64_t max_Bps,
@@ -189,9 +181,7 @@ struct mirror * mirror_create(
mirror = mirror_alloc(connect_to, mirror = mirror_alloc(connect_to,
connect_from, connect_from,
max_Bps, max_Bps, action_at_finish, commit_signal);
action_at_finish,
commit_signal);
mirror_init(mirror, filename); mirror_init(mirror, filename);
mirror_reset(mirror); mirror_reset(mirror);
@@ -281,7 +271,8 @@ int mirror_connect( struct mirror * mirror, uint64_t local_size )
NULLCHECK(mirror->connect_to); NULLCHECK(mirror->connect_to);
mirror->client = socket_connect(&mirror->connect_to->generic, connect_from); mirror->client =
socket_connect(&mirror->connect_to->generic, connect_from);
if (0 < mirror->client) { if (0 < mirror->client) {
fd_set fds; fd_set fds;
struct timeval tv = { MS_HELLO_TIME_SECS, 0 }; struct timeval tv = { MS_HELLO_TIME_SECS, 0 };
@@ -293,30 +284,30 @@ int mirror_connect( struct mirror * mirror, uint64_t local_size )
if (FD_ISSET(mirror->client, &fds)) { if (FD_ISSET(mirror->client, &fds)) {
uint64_t remote_size; uint64_t remote_size;
if ( socket_nbd_read_hello( mirror->client, &remote_size ) ) { uint32_t remote_flags;
if (socket_nbd_read_hello
(mirror->client, &remote_size, &remote_flags)) {
if (remote_size == local_size) { if (remote_size == local_size) {
connected = 1; connected = 1;
mirror_set_state(mirror, MS_GO); mirror_set_state(mirror, MS_GO);
} } else {
else {
warn("Remote size (%d) doesn't match local (%d)", warn("Remote size (%d) doesn't match local (%d)",
remote_size, local_size); remote_size, local_size);
mirror_set_state(mirror, MS_FAIL_SIZE_MISMATCH); mirror_set_state(mirror, MS_FAIL_SIZE_MISMATCH);
} }
} } else {
else {
warn("Mirror attempt rejected."); warn("Mirror attempt rejected.");
mirror_set_state(mirror, MS_FAIL_REJECTED); mirror_set_state(mirror, MS_FAIL_REJECTED);
} }
} } else {
else {
warn("No NBD Hello received."); warn("No NBD Hello received.");
mirror_set_state(mirror, MS_FAIL_NO_HELLO); mirror_set_state(mirror, MS_FAIL_NO_HELLO);
} }
if ( !connected ) { close( mirror->client ); } if (!connected) {
close(mirror->client);
} }
else { } else {
warn("Mirror failed to connect."); warn("Mirror failed to connect.");
mirror_set_state(mirror, MS_FAIL_CONNECT); mirror_set_state(mirror, MS_FAIL_CONNECT);
} }
@@ -371,12 +362,15 @@ int mirror_setup_next_xfer( struct mirror_ctrl *ctrl )
* full, and stop when it's a quarter full. This stops a busy client from * full, and stop when it's a quarter full. This stops a busy client from
* stalling a migration forever. FIXME: made-up numbers. * stalling a migration forever. FIXME: made-up numbers.
*/ */
if ( mirror->offset < serve->size && bitset_stream_size( serve->allocation_map ) > BITSET_STREAM_SIZE / 2 ) { if (mirror->offset < serve->size
&& bitset_stream_size(serve->allocation_map) >
BITSET_STREAM_SIZE / 2) {
ctrl->clear_events = 1; ctrl->clear_events = 1;
} }
while ( ( mirror->offset == serve->size || ctrl->clear_events ) && e.event != BITSET_STREAM_SET ) { while ((mirror->offset == serve->size || ctrl->clear_events)
&& e.event != BITSET_STREAM_SET) {
uint64_t events = bitset_stream_size(serve->allocation_map); uint64_t events = bitset_stream_size(serve->allocation_map);
if (events == 0) { if (events == 0) {
@@ -408,7 +402,8 @@ int mirror_setup_next_xfer( struct mirror_ctrl *ctrl )
return 0; return 0;
} }
debug( "Next transfer: current=%"PRIu64", run=%"PRIu64, current, run ); debug("Next transfer: current=%" PRIu64 ", run=%" PRIu64, current,
run);
struct nbd_request req = { struct nbd_request req = {
.magic = REQUEST_MAGIC, .magic = REQUEST_MAGIC,
.type = REQUEST_WRITE, .type = REQUEST_WRITE,
@@ -460,7 +455,8 @@ static void mirror_write_cb( struct ev_loop *loop, ev_io *w, int revents )
return; return;
} }
debug( "Mirror write callback invoked with events %d. fd: %i", revents, ctrl->mirror->client ); debug("Mirror write callback invoked with events %d. fd: %i", revents,
ctrl->mirror->client);
/* FIXME: We can end up corking multiple times in unusual circumstances; this /* FIXME: We can end up corking multiple times in unusual circumstances; this
* is annoying, but harmless */ * is annoying, but harmless */
@@ -472,7 +468,8 @@ static void mirror_write_cb( struct ev_loop *loop, ev_io *w, int revents )
data_loc = ((char *) &xfer->hdr.req_raw) + ctrl->xfer.written; data_loc = ((char *) &xfer->hdr.req_raw) + ctrl->xfer.written;
to_write = hdr_size - xfer->written; to_write = hdr_size - xfer->written;
} else { } else {
data_loc = ctrl->mirror->mapped + xfer->from + ( xfer->written - hdr_size ); data_loc =
ctrl->mirror->mapped + xfer->from + (xfer->written - hdr_size);
to_write = xfer->len - (ctrl->xfer.written - hdr_size); to_write = xfer->len - (ctrl->xfer.written - hdr_size);
} }
@@ -485,14 +482,14 @@ static void mirror_write_cb( struct ev_loop *loop, ev_io *w, int revents )
return; return;
} }
debug("Wrote %" PRIu64 " bytes", count); debug("Wrote %" PRIu64 " bytes", count);
debug( "to_write was %"PRIu64", xfer->written was %"PRIu64, to_write, xfer->written ); debug("to_write was %" PRIu64 ", xfer->written was %" PRIu64, to_write,
xfer->written);
// We wrote some bytes, so reset the timer and keep track for the next pass // We wrote some bytes, so reset the timer and keep track for the next pass
if (count > 0) { if (count > 0) {
ctrl->xfer.written += count; ctrl->xfer.written += count;
ev_timer_again(ctrl->ev_loop, &ctrl->timeout_watcher); ev_timer_again(ctrl->ev_loop, &ctrl->timeout_watcher);
} }
// All bytes written, so now we need to read the NBD reply back. // All bytes written, so now we need to read the NBD reply back.
if (ctrl->xfer.written == ctrl->xfer.len + hdr_size) { if (ctrl->xfer.written == ctrl->xfer.len + hdr_size) {
sock_set_tcp_cork(ctrl->mirror->client, 0); sock_set_tcp_cork(ctrl->mirror->client, 0);
@@ -523,10 +520,13 @@ static void mirror_read_cb( struct ev_loop *loop, ev_io *w, int revents )
ssize_t count; ssize_t count;
uint64_t left = sizeof(struct nbd_reply_raw) - xfer->read; uint64_t left = sizeof(struct nbd_reply_raw) - xfer->read;
debug( "Mirror read callback invoked with events %d. fd:%i", revents, m->client ); debug("Mirror read callback invoked with events %d. fd:%i", revents,
m->client);
/* Start / continue reading the NBD response from the mirror. */ /* Start / continue reading the NBD response from the mirror. */
if ( ( count = read( m->client, ((void*) &xfer->hdr.rsp_raw) + xfer->read, left ) ) < 0 ) { if ((count =
read(m->client, ((void *) &xfer->hdr.rsp_raw) + xfer->read,
left)) < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
warn(SHOW_ERRNO("Couldn't read from listener")); warn(SHOW_ERRNO("Couldn't read from listener"));
ev_break(loop, EVBREAK_ONE); ev_break(loop, EVBREAK_ONE);
@@ -540,12 +540,12 @@ static void mirror_read_cb( struct ev_loop *loop, ev_io *w, int revents )
ev_break(loop, EVBREAK_ONE); ev_break(loop, EVBREAK_ONE);
return; return;
} }
// We read some bytes, so reset the timer // We read some bytes, so reset the timer
ev_timer_again(ctrl->ev_loop, &ctrl->timeout_watcher); ev_timer_again(ctrl->ev_loop, &ctrl->timeout_watcher);
debug("Read %i bytes", count); debug("Read %i bytes", count);
debug( "left was %"PRIu64", xfer->read was %"PRIu64, left, xfer->read ); debug("left was %" PRIu64 ", xfer->read was %" PRIu64, left,
xfer->read);
xfer->read += count; xfer->read += count;
if (xfer->read < sizeof(struct nbd_reply_raw)) { if (xfer->read < sizeof(struct nbd_reply_raw)) {
@@ -602,7 +602,9 @@ static void mirror_read_cb( struct ev_loop *loop, ev_io *w, int revents )
debug("next_xfer: %d", next_xfer); debug("next_xfer: %d", next_xfer);
/* Regardless of time estimates, if there's no waiting transfer, we can start closing clients down. */ /* Regardless of time estimates, if there's no waiting transfer, we can start closing clients down. */
if ( !ctrl->clients_closed && ( !next_xfer || server_mirror_eta( ctrl->serve ) < MS_CONVERGE_TIME_SECS ) ) { if (!ctrl->clients_closed
&& (!next_xfer
|| server_mirror_eta(ctrl->serve) < MS_CONVERGE_TIME_SECS)) {
info("Closing clients to allow mirroring to converge"); info("Closing clients to allow mirroring to converge");
server_forbid_new_clients(ctrl->serve); server_forbid_new_clients(ctrl->serve);
server_close_clients(ctrl->serve); server_close_clients(ctrl->serve);
@@ -643,7 +645,8 @@ static void mirror_read_cb( struct ev_loop *loop, ev_io *w, int revents )
return; return;
} }
static void mirror_timeout_cb( struct ev_loop *loop, ev_timer *w __attribute__((unused)), int revents ) static void mirror_timeout_cb(struct ev_loop *loop, ev_timer * w
__attribute__ ((unused)), int revents)
{ {
if (!(revents & EV_TIMER)) { if (!(revents & EV_TIMER)) {
warn("Mirror timeout called but no timer event signalled"); warn("Mirror timeout called but no timer event signalled");
@@ -668,12 +671,14 @@ static void mirror_abandon_cb( struct ev_loop *loop, ev_io *w, int revents )
debug("Abandon message received"); debug("Abandon message received");
mirror_set_state(ctrl->mirror, MS_ABANDONED); mirror_set_state(ctrl->mirror, MS_ABANDONED);
self_pipe_signal_clear(ctrl->mirror->abandon_signal); self_pipe_signal_clear(ctrl->mirror->abandon_signal);
ev_io_stop(loop, &ctrl->abandon_watcher);
ev_break(loop, EVBREAK_ONE); ev_break(loop, EVBREAK_ONE);
return; return;
} }
static void mirror_limit_cb( struct ev_loop *loop, ev_timer *w, int revents ) static void mirror_limit_cb(struct ev_loop *loop, ev_timer * w,
int revents)
{ {
struct mirror_ctrl *ctrl = (struct mirror_ctrl *) w->data; struct mirror_ctrl *ctrl = (struct mirror_ctrl *) w->data;
NULLCHECK(ctrl); NULLCHECK(ctrl);
@@ -684,7 +689,8 @@ static void mirror_limit_cb( struct ev_loop *loop, ev_timer *w, int revents )
} }
if (mirror_should_wait(ctrl)) { if (mirror_should_wait(ctrl)) {
debug( "max_bps exceeded, waiting", ctrl->mirror->max_bytes_per_second ); debug("max_bps exceeded, waiting",
ctrl->mirror->max_bytes_per_second);
ev_timer_again(loop, w); ev_timer_again(loop, w);
} else { } else {
/* We're below the limit, so do the next request */ /* We're below the limit, so do the next request */
@@ -701,7 +707,8 @@ static void mirror_limit_cb( struct ev_loop *loop, ev_timer *w, int revents )
* if it has, start migrating. If it's not finished, then enabling the bitset * if it has, start migrating. If it's not finished, then enabling the bitset
* stream does not go well for us. * stream does not go well for us.
*/ */
static void mirror_begin_cb( struct ev_loop *loop, ev_timer *w, int revents ) static void mirror_begin_cb(struct ev_loop *loop, ev_timer * w,
int revents)
{ {
struct mirror_ctrl *ctrl = (struct mirror_ctrl *) w->data; struct mirror_ctrl *ctrl = (struct mirror_ctrl *) w->data;
NULLCHECK(ctrl); NULLCHECK(ctrl);
@@ -711,7 +718,8 @@ static void mirror_begin_cb( struct ev_loop *loop, ev_timer *w, int revents )
return; return;
} }
if ( ctrl->serve->allocation_map_built || ctrl->serve->allocation_map_not_built ) { if (ctrl->serve->allocation_map_built
|| ctrl->serve->allocation_map_not_built) {
info("allocation map builder is finished, beginning migration"); info("allocation map builder is finished, beginning migration");
ev_timer_stop(loop, w); ev_timer_stop(loop, w);
/* Start by writing xfer 0 to the listener */ /* Start by writing xfer 0 to the listener */
@@ -796,10 +804,8 @@ void mirror_run( struct server *serve )
ctrl.abandon_watcher.data = (void *) &ctrl; ctrl.abandon_watcher.data = (void *) &ctrl;
ev_io_start(ctrl.ev_loop, &ctrl.abandon_watcher); ev_io_start(ctrl.ev_loop, &ctrl.abandon_watcher);
ERROR_UNLESS( ERROR_UNLESS(mirror_setup_next_xfer(&ctrl),
mirror_setup_next_xfer( &ctrl ), "Couldn't find first transfer for mirror!");
"Couldn't find first transfer for mirror!"
);
if (serve->allocation_map_built) { if (serve->allocation_map_built) {
@@ -830,7 +836,8 @@ void mirror_run( struct server *serve )
* it to something sane - they just terminate the event loop with state != * it to something sane - they just terminate the event loop with state !=
* MS_DONE. We re-allow new clients here if necessary. * MS_DONE. We re-allow new clients here if necessary.
*/ */
if ( m->action_at_finish == ACTION_NOTHING || m->commit_state != MS_DONE ) { if (m->action_at_finish == ACTION_NOTHING
|| m->commit_state != MS_DONE) {
server_allow_new_clients(serve); server_allow_new_clients(serve);
} }
@@ -896,7 +903,9 @@ void* mirror_runner(void* serve_params_uncast)
time_t start_time = time(NULL); time_t start_time = time(NULL);
int connected = mirror_connect(mirror, serve->size); int connected = mirror_connect(mirror, serve->size);
mirror_signal_commit(mirror); mirror_signal_commit(mirror);
if ( !connected ) { goto abandon_mirror; } if (!connected) {
goto abandon_mirror;
}
/* After this point, if we see a failure we need to disconnect /* After this point, if we see a failure we need to disconnect
* and retry everything from mirror_set_state(_, MS_INIT), but * and retry everything from mirror_set_state(_, MS_INIT), but
@@ -932,38 +941,33 @@ abandon_mirror:
} }
struct mirror_super * mirror_super_create( struct mirror_super *mirror_super_create(const char *filename,
const char * filename,
union mysockaddr *connect_to, union mysockaddr *connect_to,
union mysockaddr *connect_from, union mysockaddr *connect_from,
uint64_t max_Bps, uint64_t max_Bps,
enum mirror_finish_action action_at_finish, enum mirror_finish_action
action_at_finish,
struct mbox *state_mbox) struct mbox *state_mbox)
{ {
struct mirror_super *super = xmalloc(sizeof(struct mirror_super)); struct mirror_super *super = xmalloc(sizeof(struct mirror_super));
super->mirror = mirror_create( super->mirror = mirror_create(filename,
filename,
connect_to, connect_to,
connect_from, connect_from,
max_Bps, max_Bps,
action_at_finish, action_at_finish, mbox_create());
mbox_create() ) ;
super->state_mbox = state_mbox; super->state_mbox = state_mbox;
return super; return super;
} }
/* Post the current state of the mirror into super->state_mbox.*/ /* Post the current state of the mirror into super->state_mbox.*/
void mirror_super_signal_committed( void mirror_super_signal_committed(struct mirror_super *super,
struct mirror_super * super ,
enum mirror_state commit_state) enum mirror_state commit_state)
{ {
NULLCHECK(super); NULLCHECK(super);
NULLCHECK(super->state_mbox); NULLCHECK(super->state_mbox);
mbox_post_mirror_state( mbox_post_mirror_state(super->state_mbox, commit_state);
super->state_mbox,
commit_state );
} }
@@ -997,8 +1001,7 @@ void * mirror_super_runner( void * serve_uncast )
struct mirror_super *super = serve->mirror_super; struct mirror_super *super = serve->mirror_super;
do { do {
FATAL_IF( 0 != pthread_create( FATAL_IF(0 != pthread_create(&mirror->thread,
&mirror->thread,
NULL, NULL,
mirror_runner, mirror_runner,
serve), serve),
@@ -1018,9 +1021,7 @@ void * mirror_super_runner( void * serve_uncast )
should_retry = *commit_state == MS_GO; should_retry = *commit_state == MS_GO;
/* Only send this signal the first time */ /* Only send this signal the first time */
mirror_super_signal_committed( mirror_super_signal_committed(super, *commit_state);
super,
*commit_state);
debug("Mirror supervisor committed"); debug("Mirror supervisor committed");
} }
/* We only care about the value of the commit signal on /* We only care about the value of the commit signal on

View File

@@ -127,15 +127,13 @@ struct mirror_super {
struct server; struct server;
struct flexnbd; struct flexnbd;
struct mirror_super * mirror_super_create( struct mirror_super *mirror_super_create(const char *filename,
const char * filename,
union mysockaddr *connect_to, union mysockaddr *connect_to,
union mysockaddr *connect_from, union mysockaddr *connect_from,
uint64_t max_Bps, uint64_t max_Bps,
enum mirror_finish_action action_at_finish, enum mirror_finish_action
struct mbox * state_mbox action_at_finish,
); struct mbox *state_mbox);
void *mirror_super_runner(void *serve_uncast); void *mirror_super_runner(void *serve_uncast);
#endif #endif

View File

@@ -19,6 +19,7 @@ static struct option serve_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE; static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE;
static char serve_help_text[] = static char serve_help_text[] =
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n" "Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
@@ -28,10 +29,9 @@ static char serve_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n" "\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n" "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
"\t--" OPT_KILLSWITCH",-k \tKill the server if a request takes 120 seconds.\n" "\t--" OPT_KILLSWITCH
SOCK_LINE ",-k \tKill the server if a request takes 120 seconds.\n" SOCK_LINE
VERBOSE_LINE VERBOSE_LINE QUIET_LINE;
QUIET_LINE;
static struct option listen_options[] = { static struct option listen_options[] = {
@@ -45,6 +45,7 @@ static struct option listen_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE; static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE;
static char listen_help_text[] = static char listen_help_text[] =
"Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n" "Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n"
@@ -54,9 +55,7 @@ static char listen_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n"
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n" "\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n" "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
SOCK_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
VERBOSE_LINE
QUIET_LINE;
static struct option read_options[] = { static struct option read_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -69,6 +68,7 @@ static struct option read_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE; static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE;
static char read_help_text[] = static char read_help_text[] =
"Usage: flexnbd " CMD_READ " <options>\n\n" "Usage: flexnbd " CMD_READ " <options>\n\n"
@@ -78,9 +78,7 @@ static char read_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n" "\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n" "\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n"
BIND_LINE BIND_LINE VERBOSE_LINE QUIET_LINE;
VERBOSE_LINE
QUIET_LINE;
static struct option *write_options = read_options; static struct option *write_options = read_options;
@@ -93,9 +91,7 @@ static char write_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n" "\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n" "\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n"
BIND_LINE BIND_LINE VERBOSE_LINE QUIET_LINE;
VERBOSE_LINE
QUIET_LINE;
static struct option acl_options[] = { static struct option acl_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -104,14 +100,12 @@ static struct option acl_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char acl_help_text[] = static char acl_help_text[] =
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n" "Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
"Set the access control list for a server with control socket SOCK.\n\n" "Set the access control list for a server with control socket SOCK.\n\n"
HELP_LINE HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option mirror_speed_options[] = { static struct option mirror_speed_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -121,15 +115,12 @@ static struct option mirror_speed_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE; static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE;
static char mirror_speed_help_text[] = static char mirror_speed_help_text[] =
"Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n" "Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n"
"Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n" "Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n"
HELP_LINE HELP_LINE SOCK_LINE MAX_SPEED_LINE VERBOSE_LINE QUIET_LINE;
SOCK_LINE
MAX_SPEED_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option mirror_options[] = { static struct option mirror_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -142,6 +133,7 @@ static struct option mirror_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE; static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE;
static char mirror_help_text[] = static char mirror_help_text[] =
"Usage: flexnbd " CMD_MIRROR " <options>\n\n" "Usage: flexnbd " CMD_MIRROR " <options>\n\n"
@@ -151,9 +143,7 @@ static char mirror_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n"
SOCK_LINE SOCK_LINE
"\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n" "\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n"
BIND_LINE BIND_LINE VERBOSE_LINE QUIET_LINE;
VERBOSE_LINE
QUIET_LINE;
static struct option break_options[] = { static struct option break_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -162,14 +152,12 @@ static struct option break_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char break_help_text[] = static char break_help_text[] =
"Usage: flexnbd " CMD_BREAK " <options>\n\n" "Usage: flexnbd " CMD_BREAK " <options>\n\n"
"Stop mirroring from the server with control socket SOCK.\n\n" "Stop mirroring from the server with control socket SOCK.\n\n"
HELP_LINE HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option status_options[] = { static struct option status_options[] = {
@@ -179,14 +167,12 @@ static struct option status_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char status_help_text[] = static char status_help_text[] =
"Usage: flexnbd " CMD_STATUS " <options>\n\n" "Usage: flexnbd " CMD_STATUS " <options>\n\n"
"Get the status for a server with control socket SOCK.\n\n" "Get the status for a server with control socket SOCK.\n\n"
HELP_LINE HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
char help_help_text_arr[] = char help_help_text_arr[] =
"Usage: flexnbd <cmd> [cmd options]\n\n" "Usage: flexnbd <cmd> [cmd options]\n\n"
@@ -200,8 +186,7 @@ char help_help_text_arr[] =
"\tflexnbd mirror-speed\n" "\tflexnbd mirror-speed\n"
"\tflexnbd break\n" "\tflexnbd break\n"
"\tflexnbd status\n" "\tflexnbd status\n"
"\tflexnbd help\n\n" "\tflexnbd help\n\n" "See flexnbd help <cmd> for further info\n";
"See flexnbd help <cmd> for further info\n";
/* Slightly odd array/pointer pair to stop the compiler from complaining /* Slightly odd array/pointer pair to stop the compiler from complaining
* about symbol sizes * about symbol sizes
*/ */
@@ -214,7 +199,8 @@ void do_write(struct mode_readwrite_params* params);
void do_remote_command(char *command, char *mode, int argc, char **argv); void do_remote_command(char *command, char *mode, int argc, char **argv);
void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char **sock, int *default_deny, int *use_killswitch ) void read_serve_param(int c, char **ip_addr, char **ip_port, char **file,
char **sock, int *default_deny, int *use_killswitch)
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -254,9 +240,7 @@ void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char
void read_listen_param(int c, void read_listen_param(int c,
char **ip_addr, char **ip_addr,
char **ip_port, char **ip_port,
char **file, char **file, char **sock, int *default_deny)
char **sock,
int *default_deny )
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -289,7 +273,9 @@ void read_listen_param( int c,
} }
} }
void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **bind_addr, char **from, char **size, char *err_text ) void read_readwrite_param(int c, char **ip_addr, char **ip_port,
char **bind_addr, char **from, char **size,
char *err_text)
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -348,11 +334,7 @@ void read_acl_param( int c, char **sock )
read_sock_param(c, sock, acl_help_text); read_sock_param(c, sock, acl_help_text);
} }
void read_mirror_speed_param( void read_mirror_speed_param(int c, char **sock, char **max_speed)
int c,
char **sock,
char **max_speed
)
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -377,13 +359,10 @@ void read_mirror_speed_param(
} }
} }
void read_mirror_param( void read_mirror_param(int c,
int c,
char **sock, char **sock,
char **ip_addr, char **ip_addr,
char **ip_port, char **ip_port, int *unlink, char **bind_addr)
int *unlink,
char **bind_addr )
{ {
switch (c) { switch (c) {
case 'h': case 'h':
@@ -459,10 +438,14 @@ int mode_serve( int argc, char *argv[] )
struct flexnbd *flexnbd; struct flexnbd *flexnbd;
while (1) { while (1) {
c = getopt_long(argc, argv, serve_short_options, serve_options, NULL); c = getopt_long(argc, argv, serve_short_options, serve_options,
if ( c == -1 ) { break; } NULL);
if (c == -1) {
break;
}
read_serve_param( c, &ip_addr, &ip_port, &file, &sock, &default_deny, &use_killswitch ); read_serve_param(c, &ip_addr, &ip_port, &file, &sock,
&default_deny, &use_killswitch);
} }
if (NULL == ip_addr || NULL == ip_port) { if (NULL == ip_addr || NULL == ip_port) {
@@ -473,9 +456,14 @@ int mode_serve( int argc, char *argv[] )
err = 1; err = 1;
fprintf(stderr, "--file is required\n"); fprintf(stderr, "--file is required\n");
} }
if ( err ) { exit_err( serve_help_text ); } if (err) {
exit_err(serve_help_text);
}
flexnbd = flexnbd_create_serving( ip_addr, ip_port, file, sock, default_deny, argc - optind, argv + optind, MAX_NBD_CLIENTS, use_killswitch ); flexnbd =
flexnbd_create_serving(ip_addr, ip_port, file, sock, default_deny,
argc - optind, argv + optind,
MAX_NBD_CLIENTS, use_killswitch);
info("Serving file %s", file); info("Serving file %s", file);
success = flexnbd_serve(flexnbd); success = flexnbd_serve(flexnbd);
flexnbd_destroy(flexnbd); flexnbd_destroy(flexnbd);
@@ -499,8 +487,11 @@ int mode_listen( int argc, char *argv[] )
struct flexnbd *flexnbd; struct flexnbd *flexnbd;
while (1) { while (1) {
c = getopt_long(argc, argv, listen_short_options, listen_options, NULL); c = getopt_long(argc, argv, listen_short_options, listen_options,
if ( c == -1 ) { break; } NULL);
if (c == -1) {
break;
}
read_listen_param(c, &ip_addr, &ip_port, read_listen_param(c, &ip_addr, &ip_port,
&file, &sock, &default_deny); &file, &sock, &default_deny);
@@ -514,16 +505,16 @@ int mode_listen( int argc, char *argv[] )
err = 1; err = 1;
fprintf(stderr, "--file is required\n"); fprintf(stderr, "--file is required\n");
} }
if ( err ) { exit_err( listen_help_text ); } if (err) {
exit_err(listen_help_text);
}
flexnbd = flexnbd_create_listening( flexnbd = flexnbd_create_listening(ip_addr,
ip_addr,
ip_port, ip_port,
file, file,
sock, sock,
default_deny, default_deny,
argc - optind, argc - optind, argv + optind);
argv + optind);
success = flexnbd_serve(flexnbd); success = flexnbd_serve(flexnbd);
flexnbd_destroy(flexnbd); flexnbd_destroy(flexnbd);
@@ -545,29 +536,25 @@ int mode_listen( int argc, char *argv[] )
* char *s_length, * char *s_length,
* char *s_filename ) * char *s_filename )
*/ */
void params_readwrite( void params_readwrite(int write_not_read,
int write_not_read,
struct mode_readwrite_params *out, struct mode_readwrite_params *out,
char *s_ip_address, char *s_ip_address,
char *s_port, char *s_port,
char *s_bind_address, char *s_bind_address,
char* s_from, char *s_from, char *s_length_or_filename)
char* s_length_or_filename
)
{ {
FATAL_IF_NULL(s_ip_address, "No IP address supplied"); FATAL_IF_NULL(s_ip_address, "No IP address supplied");
FATAL_IF_NULL(s_port, "No port number supplied"); FATAL_IF_NULL(s_port, "No port number supplied");
FATAL_IF_NULL(s_from, "No from supplied"); FATAL_IF_NULL(s_from, "No from supplied");
FATAL_IF_NULL(s_length_or_filename, "No length supplied"); FATAL_IF_NULL(s_length_or_filename, "No length supplied");
FATAL_IF_ZERO( FATAL_IF_ZERO(parse_ip_to_sockaddr
parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address), (&out->connect_to.generic, s_ip_address),
"Couldn't parse connection address '%s'", "Couldn't parse connection address '%s'", s_ip_address);
s_ip_address
);
if (s_bind_address != NULL && if (s_bind_address != NULL &&
parse_ip_to_sockaddr(&out->connect_from.generic, s_bind_address) == 0) { parse_ip_to_sockaddr(&out->connect_from.generic,
s_bind_address) == 0) {
fatal("Couldn't parse bind address '%s'", s_bind_address); fatal("Couldn't parse bind address '%s'", s_bind_address);
} }
@@ -575,30 +562,27 @@ void params_readwrite(
long signed_from = atol(s_from); long signed_from = atol(s_from);
FATAL_IF_NEGATIVE(signed_from, FATAL_IF_NEGATIVE(signed_from,
"Can't read from a negative offset %d.", signed_from); "Can't read from a negative offset %d.",
signed_from);
out->from = signed_from; out->from = signed_from;
if (write_not_read) { if (write_not_read) {
if (s_length_or_filename[0] - 48 < 10) { if (s_length_or_filename[0] - 48 < 10) {
out->len = atol(s_length_or_filename); out->len = atol(s_length_or_filename);
out->data_fd = 0; out->data_fd = 0;
} } else {
else { out->data_fd = open(s_length_or_filename, O_RDONLY);
out->data_fd = open(
s_length_or_filename, O_RDONLY);
FATAL_IF_NEGATIVE(out->data_fd, FATAL_IF_NEGATIVE(out->data_fd,
"Couldn't open %s", s_length_or_filename); "Couldn't open %s", s_length_or_filename);
off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END); off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END);
FATAL_IF_NEGATIVE(signed_len, FATAL_IF_NEGATIVE(signed_len,
"Couldn't find length of %s", s_length_or_filename); "Couldn't find length of %s",
s_length_or_filename);
out->len = signed_len; out->len = signed_len;
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(lseek64(out->data_fd, 0, SEEK_SET),
lseek64(out->data_fd, 0, SEEK_SET), "Couldn't rewind %s", s_length_or_filename);
"Couldn't rewind %s", s_length_or_filename
);
} }
} } else {
else {
out->len = atol(s_length_or_filename); out->len = atol(s_length_or_filename);
out->data_fd = 1; out->data_fd = 1;
} }
@@ -618,11 +602,15 @@ int mode_read( int argc, char *argv[] )
struct mode_readwrite_params readwrite; struct mode_readwrite_params readwrite;
while (1) { while (1) {
c = getopt_long(argc, argv, read_short_options, read_options, NULL); c = getopt_long(argc, argv, read_short_options, read_options,
NULL);
if ( c == -1 ) { break; } if (c == -1) {
break;
}
read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, read_help_text ); read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from,
&size, read_help_text);
} }
if (NULL == ip_addr || NULL == ip_port) { if (NULL == ip_addr || NULL == ip_port) {
@@ -633,10 +621,13 @@ int mode_read( int argc, char *argv[] )
err = 1; err = 1;
fprintf(stderr, "both --from and --size are required.\n"); fprintf(stderr, "both --from and --size are required.\n");
} }
if ( err ) { exit_err( read_help_text ); } if (err) {
exit_err(read_help_text);
}
memset(&readwrite, 0, sizeof(readwrite)); memset(&readwrite, 0, sizeof(readwrite));
params_readwrite( 0, &readwrite, ip_addr, ip_port, bind_addr, from, size ); params_readwrite(0, &readwrite, ip_addr, ip_port, bind_addr, from,
size);
do_read(&readwrite); do_read(&readwrite);
return 0; return 0;
} }
@@ -654,10 +645,14 @@ int mode_write( int argc, char *argv[] )
struct mode_readwrite_params readwrite; struct mode_readwrite_params readwrite;
while (1) { while (1) {
c = getopt_long(argc, argv, write_short_options, write_options, NULL); c = getopt_long(argc, argv, write_short_options, write_options,
if ( c == -1 ) { break; } NULL);
if (c == -1) {
break;
}
read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, write_help_text ); read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from,
&size, write_help_text);
} }
if (NULL == ip_addr || NULL == ip_port) { if (NULL == ip_addr || NULL == ip_port) {
@@ -668,10 +663,13 @@ int mode_write( int argc, char *argv[] )
err = 1; err = 1;
fprintf(stderr, "both --from and --size are required.\n"); fprintf(stderr, "both --from and --size are required.\n");
} }
if ( err ) { exit_err( write_help_text ); } if (err) {
exit_err(write_help_text);
}
memset(&readwrite, 0, sizeof(readwrite)); memset(&readwrite, 0, sizeof(readwrite));
params_readwrite( 1, &readwrite, ip_addr, ip_port, bind_addr, from, size ); params_readwrite(1, &readwrite, ip_addr, ip_port, bind_addr, from,
size);
do_write(&readwrite); do_write(&readwrite);
return 0; return 0;
} }
@@ -683,7 +681,9 @@ int mode_acl( int argc, char *argv[] )
while (1) { while (1) {
c = getopt_long(argc, argv, acl_short_options, acl_options, NULL); c = getopt_long(argc, argv, acl_short_options, acl_options, NULL);
if ( c == -1 ) { break; } if (c == -1) {
break;
}
read_acl_param(c, &sock); read_acl_param(c, &sock);
} }
@@ -708,8 +708,11 @@ int mode_mirror_speed( int argc, char *argv[] )
char *speed = NULL; char *speed = NULL;
while (1) { while (1) {
c = getopt_long( argc, argv, mirror_speed_short_options, mirror_speed_options, NULL ); c = getopt_long(argc, argv, mirror_speed_short_options,
if ( -1 == c ) { break; } mirror_speed_options, NULL);
if (-1 == c) {
break;
}
read_mirror_speed_param(c, &sock, &speed); read_mirror_speed_param(c, &sock, &speed);
} }
@@ -739,14 +742,15 @@ int mode_mirror( int argc, char *argv[] )
remote_argv[2] = "exit"; remote_argv[2] = "exit";
while (1) { while (1) {
c = getopt_long( argc, argv, mirror_short_options, mirror_options, NULL); c = getopt_long(argc, argv, mirror_short_options, mirror_options,
if ( -1 == c ) { break; } NULL);
if (-1 == c) {
break;
}
read_mirror_param(c, read_mirror_param(c,
&sock, &sock,
&remote_argv[0], &remote_argv[0],
&remote_argv[1], &remote_argv[1], &unlink, &remote_argv[3]);
&unlink,
&remote_argv[3] );
} }
if (NULL == sock) { if (NULL == sock) {
@@ -757,13 +761,16 @@ int mode_mirror( int argc, char *argv[] )
fprintf(stderr, "both --addr and --port are required.\n"); fprintf(stderr, "both --addr and --port are required.\n");
err = 1; err = 1;
} }
if ( err ) { exit_err( mirror_help_text ); } if (err) {
if ( unlink ) { remote_argv[2] = "unlink"; } exit_err(mirror_help_text);
}
if (unlink) {
remote_argv[2] = "unlink";
}
if (remote_argv[3] == NULL) { if (remote_argv[3] == NULL) {
do_remote_command("mirror", sock, 3, remote_argv); do_remote_command("mirror", sock, 3, remote_argv);
} } else {
else {
do_remote_command("mirror", sock, 4, remote_argv); do_remote_command("mirror", sock, 4, remote_argv);
} }
@@ -777,8 +784,11 @@ int mode_break( int argc, char *argv[] )
char *sock = NULL; char *sock = NULL;
while (1) { while (1) {
c = getopt_long( argc, argv, break_short_options, break_options, NULL ); c = getopt_long(argc, argv, break_short_options, break_options,
if ( -1 == c ) { break; } NULL);
if (-1 == c) {
break;
}
read_break_param(c, &sock); read_break_param(c, &sock);
} }
@@ -798,8 +808,11 @@ int mode_status( int argc, char *argv[] )
char *sock = NULL; char *sock = NULL;
while (1) { while (1) {
c = getopt_long( argc, argv, status_short_options, status_options, NULL ); c = getopt_long(argc, argv, status_short_options, status_options,
if ( -1 == c ) { break; } NULL);
if (-1 == c) {
break;
}
read_status_param(c, &sock); read_status_param(c, &sock);
} }
@@ -836,7 +849,9 @@ int mode_help( int argc, char *argv[] )
help_text = mirror_help_text; help_text = mirror_help_text;
} else if (IS_CMD(CMD_STATUS, cmd)) { } else if (IS_CMD(CMD_STATUS, cmd)) {
help_text = status_help_text; help_text = status_help_text;
} else { exit_err( help_help_text ); } } else {
exit_err(help_help_text);
}
} }
fprintf(stdout, "%s\n", help_text); fprintf(stdout, "%s\n", help_text);
@@ -848,37 +863,27 @@ void mode(char* mode, int argc, char **argv)
{ {
if (IS_CMD(CMD_SERVE, mode)) { if (IS_CMD(CMD_SERVE, mode)) {
exit(mode_serve(argc, argv)); exit(mode_serve(argc, argv));
} } else if (IS_CMD(CMD_LISTEN, mode)) {
else if ( IS_CMD( CMD_LISTEN, mode ) ) {
exit(mode_listen(argc, argv)); exit(mode_listen(argc, argv));
} } else if (IS_CMD(CMD_READ, mode)) {
else if ( IS_CMD( CMD_READ, mode ) ) {
mode_read(argc, argv); mode_read(argc, argv);
} } else if (IS_CMD(CMD_WRITE, mode)) {
else if ( IS_CMD( CMD_WRITE, mode ) ) {
mode_write(argc, argv); mode_write(argc, argv);
} } else if (IS_CMD(CMD_ACL, mode)) {
else if ( IS_CMD( CMD_ACL, mode ) ) {
mode_acl(argc, argv); mode_acl(argc, argv);
} else if (IS_CMD(CMD_MIRROR_SPEED, mode)) { } else if (IS_CMD(CMD_MIRROR_SPEED, mode)) {
mode_mirror_speed(argc, argv); mode_mirror_speed(argc, argv);
} } else if (IS_CMD(CMD_MIRROR, mode)) {
else if ( IS_CMD( CMD_MIRROR, mode ) ) {
mode_mirror(argc, argv); mode_mirror(argc, argv);
} } else if (IS_CMD(CMD_BREAK, mode)) {
else if ( IS_CMD( CMD_BREAK, mode ) ) {
mode_break(argc, argv); mode_break(argc, argv);
} } else if (IS_CMD(CMD_STATUS, mode)) {
else if ( IS_CMD( CMD_STATUS, mode ) ) {
mode_status(argc, argv); mode_status(argc, argv);
} } else if (IS_CMD(CMD_HELP, mode)) {
else if ( IS_CMD( CMD_HELP, mode ) ) {
mode_help(argc - 1, argv + 1); mode_help(argc - 1, argv + 1);
} } else {
else {
mode_help(argc - 1, argv + 1); mode_help(argc - 1, argv + 1);
exit(1); exit(1);
} }
exit(0); exit(0);
} }

View File

@@ -21,8 +21,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
struct server * server_create ( struct server *server_create(struct flexnbd *flexnbd,
struct flexnbd * flexnbd,
char *s_ip_address, char *s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
@@ -30,8 +29,7 @@ struct server * server_create (
int acl_entries, int acl_entries,
char **s_acl_entries, char **s_acl_entries,
int max_nbd_clients, int max_nbd_clients,
int use_killswitch, int use_killswitch, int success)
int success)
{ {
NULLCHECK(flexnbd); NULLCHECK(flexnbd);
struct server *out; struct server *out;
@@ -43,19 +41,18 @@ struct server * server_create (
server_allow_new_clients(out); server_allow_new_clients(out);
out->nbd_client = xmalloc( max_nbd_clients * sizeof( struct client_tbl_entry ) ); out->nbd_client =
xmalloc(max_nbd_clients * sizeof(struct client_tbl_entry));
out->tcp_backlog = 10; /* does this need to be settable? */ out->tcp_backlog = 10; /* does this need to be settable? */
FATAL_IF_NULL(s_ip_address, "No IP address supplied"); FATAL_IF_NULL(s_ip_address, "No IP address supplied");
FATAL_IF_NULL(s_port, "No port number supplied"); FATAL_IF_NULL(s_port, "No port number supplied");
FATAL_IF_NULL(s_file, "No filename supplied"); FATAL_IF_NULL(s_file, "No filename supplied");
NULLCHECK(s_ip_address); NULLCHECK(s_ip_address);
FATAL_IF_ZERO( FATAL_IF_ZERO(parse_ip_to_sockaddr
parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address), (&out->bind_to.generic, s_ip_address),
"Couldn't parse server address '%s' (use 0 if " "Couldn't parse server address '%s' (use 0 if "
"you want to bind to all IPs)", "you want to bind to all IPs)", s_ip_address);
s_ip_address
);
out->acl = acl_create(acl_entries, s_acl_entries, default_deny); out->acl = acl_create(acl_entries, s_acl_entries, default_deny);
@@ -110,8 +107,7 @@ void server_unlink( struct server * serve )
FATAL_IF_NEGATIVE(unlink(serve->filename), FATAL_IF_NEGATIVE(unlink(serve->filename),
"Failed to unlink %s: %s", "Failed to unlink %s: %s",
serve->filename, serve->filename, strerror(errno));
strerror( errno ) );
} }
@@ -155,7 +151,8 @@ void server_unlock_start_mirror( struct server *serve )
{ {
debug("Mirror start unlocking"); debug("Mirror start unlocking");
SERVER_UNLOCK( serve, l_start_mirror, "Problem with start mirror unlock" ); SERVER_UNLOCK(serve, l_start_mirror,
"Problem with start mirror unlock");
} }
int server_start_mirror_locked(struct server *serve) int server_start_mirror_locked(struct server *serve)
@@ -186,8 +183,9 @@ void serve_open_server_socket(struct server* params)
{ {
NULLCHECK(params); NULLCHECK(params);
params->server_fd = socket(params->bind_to.generic.sa_family == AF_INET ? params->server_fd =
PF_INET : PF_INET6, SOCK_STREAM, 0); socket(params->bind_to.generic.sa_family ==
AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0);
FATAL_IF_NEGATIVE(params->server_fd, "Couldn't create server socket"); FATAL_IF_NEGATIVE(params->server_fd, "Couldn't create server socket");
@@ -199,35 +197,32 @@ void serve_open_server_socket(struct server* params)
* problem. It's also indicative of something odd going on, so * problem. It's also indicative of something odd going on, so
* we barf. * we barf.
*/ */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_set_reuseaddr(params->server_fd, 1),
sock_set_reuseaddr( params->server_fd, 1 ), "Couldn't set SO_REUSEADDR" "Couldn't set SO_REUSEADDR");
);
/* TCP_NODELAY makes everything not be slow. If we can't set /* TCP_NODELAY makes everything not be slow. If we can't set
* this, again, there's something odd going on which we don't * this, again, there's something odd going on which we don't
* understand. * understand.
*/ */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_set_tcp_nodelay(params->server_fd, 1),
sock_set_tcp_nodelay( params->server_fd, 1 ), "Couldn't set TCP_NODELAY" "Couldn't set TCP_NODELAY");
);
/* If we can't bind, presumably that's because someone else is /* If we can't bind, presumably that's because someone else is
* squatting on our ip/port combo, or the ip isn't yet * squatting on our ip/port combo, or the ip isn't yet
* configured. Ideally we want to retry this. */ * configured. Ideally we want to retry this. */
FATAL_UNLESS_ZERO( FATAL_UNLESS_ZERO(sock_try_bind
sock_try_bind( params->server_fd, &params->bind_to.generic ), (params->server_fd, &params->bind_to.generic),
SHOW_ERRNO("Failed to bind() socket") SHOW_ERRNO("Failed to bind() socket")
); );
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(listen(params->server_fd, params->tcp_backlog),
listen(params->server_fd, params->tcp_backlog), "Couldn't listen on server socket");
"Couldn't listen on server socket"
);
} }
int tryjoin_client_thread( struct client_tbl_entry *entry, int (*joinfunc)(pthread_t, void **) ) int tryjoin_client_thread(struct client_tbl_entry *entry,
int (*joinfunc) (pthread_t, void **))
{ {
NULLCHECK(entry); NULLCHECK(entry);
@@ -239,25 +234,25 @@ int tryjoin_client_thread( struct client_tbl_entry *entry, int (*joinfunc)(pthre
if (entry->thread != 0) { if (entry->thread != 0) {
char s_client_address[128]; char s_client_address[128];
sockaddr_address_string( &entry->address.generic, &s_client_address[0], 128 ); sockaddr_address_string(&entry->address.generic,
&s_client_address[0], 128);
debug( "%s(%p,...)", joinfunc == pthread_join ? "joining" : "tryjoining", entry->thread ); debug("%s(%p,...)",
joinfunc == pthread_join ? "joining" : "tryjoining",
entry->thread);
int join_errno = joinfunc(entry->thread, &status); int join_errno = joinfunc(entry->thread, &status);
/* join_errno can legitimately be ESRCH if the thread is /* join_errno can legitimately be ESRCH if the thread is
* already dead, but the client still needs tidying up. */ * already dead, but the client still needs tidying up. */
if (join_errno != 0 && !entry->client->stopped) { if (join_errno != 0 && !entry->client->stopped) {
debug( "join_errno was %s, stopped was %d", strerror( join_errno ), entry->client->stopped ); debug("join_errno was %s, stopped was %d",
strerror(join_errno), entry->client->stopped);
FATAL_UNLESS(join_errno == EBUSY, FATAL_UNLESS(join_errno == EBUSY,
"Problem with joining thread %p: %s", "Problem with joining thread %p: %s",
entry->thread, entry->thread, strerror(join_errno));
strerror(join_errno) ); } else if (join_errno == 0) {
}
else if ( join_errno == 0 ) {
debug("nbd thread %016x exited (%s) with status %ld", debug("nbd thread %016x exited (%s) with status %ld",
entry->thread, entry->thread, s_client_address, (uintptr_t) status);
s_client_address,
(uintptr_t)status);
client_destroy(entry->client); client_destroy(entry->client);
entry->client = NULL; entry->client = NULL;
entry->thread = 0; entry->thread = 0;
@@ -287,7 +282,8 @@ int cleanup_client_thread( struct client_tbl_entry * entry )
return tryjoin_client_thread(entry, pthread_tryjoin_np); return tryjoin_client_thread(entry, pthread_tryjoin_np);
} }
void cleanup_client_threads( struct client_tbl_entry * entries, size_t entries_len ) void cleanup_client_threads(struct client_tbl_entry *entries,
size_t entries_len)
{ {
size_t i; size_t i;
for (i = 0; i < entries_len; i++) { for (i = 0; i < entries_len; i++) {
@@ -333,6 +329,8 @@ int server_count_clients( struct server *params )
NULLCHECK(params); NULLCHECK(params);
int i, count = 0; int i, count = 0;
cleanup_client_threads(params->nbd_client, params->max_nbd_clients);
for (i = 0; i < params->max_nbd_clients; i++) { for (i = 0; i < params->max_nbd_clients; i++) {
if (params->nbd_client[i].thread != 0) { if (params->nbd_client[i].thread != 0) {
count++; count++;
@@ -347,7 +345,8 @@ int server_count_clients( struct server *params )
* to the current acl. If params->acl is NULL, the result will be 1, * to the current acl. If params->acl is NULL, the result will be 1,
* otherwise it will be the result of acl_includes(). * otherwise it will be the result of acl_includes().
*/ */
int server_acl_accepts( struct server *params, union mysockaddr * client_address ) int server_acl_accepts(struct server *params,
union mysockaddr *client_address)
{ {
NULLCHECK(params); NULLCHECK(params);
NULLCHECK(client_address); NULLCHECK(client_address);
@@ -366,8 +365,7 @@ int server_acl_accepts( struct server *params, union mysockaddr * client_address
} }
int server_should_accept_client( int server_should_accept_client(struct server *params,
struct server * params,
union mysockaddr *client_address, union mysockaddr *client_address,
char *s_client_address, char *s_client_address,
size_t s_client_address_len) size_t s_client_address_len)
@@ -376,9 +374,9 @@ int server_should_accept_client(
NULLCHECK(client_address); NULLCHECK(client_address);
NULLCHECK(s_client_address); NULLCHECK(s_client_address);
const char* result = sockaddr_address_string( const char *result =
&client_address->generic, s_client_address, s_client_address_len sockaddr_address_string(&client_address->generic, s_client_address,
); s_client_address_len);
if (NULL == result) { if (NULL == result) {
warn("Rejecting client %s: Bad client_address", s_client_address); warn("Rejecting client %s: Bad client_address", s_client_address);
@@ -386,7 +384,8 @@ int server_should_accept_client(
} }
if (!server_acl_accepts(params, client_address)) { if (!server_acl_accepts(params, client_address)) {
warn( "Rejecting client %s: Access control error", s_client_address ); warn("Rejecting client %s: Access control error",
s_client_address);
debug("We %s have an acl, and default_deny is %s", debug("We %s have an acl, and default_deny is %s",
(params->acl ? "do" : "do not"), (params->acl ? "do" : "do not"),
(params->acl->default_deny ? "true" : "false")); (params->acl->default_deny ? "true" : "false"));
@@ -398,11 +397,11 @@ int server_should_accept_client(
int spawn_client_thread( int spawn_client_thread(struct client *client_params,
struct client * client_params,
pthread_t * out_thread) pthread_t * out_thread)
{ {
int result = pthread_create(out_thread, NULL, client_serve, client_params); int result =
pthread_create(out_thread, NULL, client_serve, client_params);
return result; return result;
} }
@@ -412,10 +411,8 @@ int spawn_client_thread(
* to handle it. Rejects the connection if there is an ACL, and the far end's * to handle it. Rejects the connection if there is an ACL, and the far end's
* address doesn't match, or if there are too many clients already connected. * address doesn't match, or if there are too many clients already connected.
*/ */
void accept_nbd_client( void accept_nbd_client(struct server *params,
struct server* params, int client_fd, union mysockaddr *client_address)
int client_fd,
union mysockaddr* client_address)
{ {
NULLCHECK(params); NULLCHECK(params);
NULLCHECK(client_address); NULLCHECK(client_address);
@@ -424,11 +421,15 @@ void accept_nbd_client(
int slot; int slot;
char s_client_address[64] = { 0 }; char s_client_address[64] = { 0 };
FATAL_IF_NEGATIVE( sock_set_keepalive_params( client_fd, CLIENT_KEEPALIVE_TIME, CLIENT_KEEPALIVE_INTVL, CLIENT_KEEPALIVE_PROBES), FATAL_IF_NEGATIVE(sock_set_keepalive_params
"Error setting keepalive parameters on client socket fd %d", client_fd ); (client_fd, CLIENT_KEEPALIVE_TIME,
CLIENT_KEEPALIVE_INTVL, CLIENT_KEEPALIVE_PROBES),
"Error setting keepalive parameters on client socket fd %d",
client_fd);
if ( !server_should_accept_client( params, client_address, s_client_address, 64 ) ) { if (!server_should_accept_client
(params, client_address, s_client_address, 64)) {
FATAL_IF_NEGATIVE(close(client_fd), FATAL_IF_NEGATIVE(close(client_fd),
"Error closing client socket fd %d", client_fd); "Error closing client socket fd %d", client_fd);
debug("Closed client socket fd %d", client_fd); debug("Closed client socket fd %d", client_fd);
@@ -462,7 +463,8 @@ void accept_nbd_client(
return; return;
} }
debug("nbd thread %p started (%s)", params->nbd_client[slot].thread, s_client_address); debug("nbd thread %p started (%s)", params->nbd_client[slot].thread,
s_client_address);
} }
@@ -483,8 +485,12 @@ void server_audit_clients( struct server * serve)
*/ */
for (i = 0; i < serve->max_nbd_clients; i++) { for (i = 0; i < serve->max_nbd_clients; i++) {
entry = &serve->nbd_client[i]; entry = &serve->nbd_client[i];
if ( 0 == entry->thread ) { continue; } if (0 == entry->thread) {
if ( server_acl_accepts( serve, &entry->address ) ) { continue; } continue;
}
if (server_acl_accepts(serve, &entry->address)) {
continue;
}
client_signal_stop(entry->client); client_signal_stop(entry->client);
} }
} }
@@ -544,7 +550,9 @@ void server_replace_acl( struct server *serve, struct acl * new_acl )
struct acl *old_acl = serve->acl; struct acl *old_acl = serve->acl;
serve->acl = new_acl; serve->acl = new_acl;
/* We should always have an old_acl, but just in case... */ /* We should always have an old_acl, but just in case... */
if ( old_acl ) { acl_destroy( old_acl ); } if (old_acl) {
acl_destroy(old_acl);
}
} }
server_unlock_acl(serve); server_unlock_acl(serve);
@@ -612,12 +620,13 @@ int server_accept( struct server * params )
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(params->server_fd, &fds); FD_SET(params->server_fd, &fds);
if( 0 < signal_fd ) { FD_SET(signal_fd, &fds); } if (0 < signal_fd) {
FD_SET(signal_fd, &fds);
}
self_pipe_fd_set(params->close_signal, &fds); self_pipe_fd_set(params->close_signal, &fds);
self_pipe_fd_set(params->acl_updated_signal, &fds); self_pipe_fd_set(params->acl_updated_signal, &fds);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
SHOW_ERRNO("select() failed") SHOW_ERRNO("select() failed")
); );
@@ -630,7 +639,8 @@ int server_accept( struct server * params )
if (0 < signal_fd && FD_ISSET(signal_fd, &fds)) { if (0 < signal_fd && FD_ISSET(signal_fd, &fds)) {
debug("Stop signal received."); debug("Stop signal received.");
server_close_clients(params); server_close_clients(params);
params->success = params->success && serve_shutdown_is_graceful( params ); params->success = params->success
&& serve_shutdown_is_graceful(params);
should_continue = 0; should_continue = 0;
} }
@@ -641,7 +651,8 @@ int server_accept( struct server * params )
} }
if (FD_ISSET(params->server_fd, &fds)) { if (FD_ISSET(params->server_fd, &fds)) {
int client_fd = accept( params->server_fd, &client_address.generic, &socklen ); int client_fd =
accept(params->server_fd, &client_address.generic, &socklen);
if (params->allow_new_clients) { if (params->allow_new_clients) {
debug("Accepted nbd client socket fd %d", client_fd); debug("Accepted nbd client socket fd %d", client_fd);
@@ -676,8 +687,7 @@ void* build_allocation_map_thread(void* serve_uncast)
if (build_allocation_map(serve->allocation_map, fd)) { if (build_allocation_map(serve->allocation_map, fd)) {
serve->allocation_map_built = 1; serve->allocation_map_built = 1;
} } else {
else {
/* We can operate without it, but we can't free it without a race. /* We can operate without it, but we can't free it without a race.
* All that happens if we leave it is that it gradually builds up an * All that happens if we leave it is that it gradually builds up an
* *incomplete* record of writes. Nobody will use it, as * *incomplete* record of writes. Nobody will use it, as
@@ -710,9 +720,17 @@ void serve_init_allocation_map(struct server* params)
FATAL_IF_NEGATIVE(fd, "Couldn't open %s", params->filename); FATAL_IF_NEGATIVE(fd, "Couldn't open %s", params->filename);
size = lseek64(fd, 0, SEEK_END); size = lseek64(fd, 0, SEEK_END);
/* If discs are not in multiples of 512, then odd things happen,
* resulting in reads/writes past the ends of files.
*/
if (size != (size & ~0x1ff)) {
warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
size &= ~0x1ff;
}
params->size = size; params->size = size;
FATAL_IF_NEGATIVE( size, "Couldn't find size of %s", FATAL_IF_NEGATIVE(size, "Couldn't find size of %s", params->filename);
params->filename );
params->allocation_map = params->allocation_map =
bitset_alloc(params->size, block_allocation_resolution); bitset_alloc(params->size, block_allocation_resolution);
@@ -738,7 +756,8 @@ void server_allow_new_clients( struct server * serve )
return; return;
} }
void server_join_clients( struct server * serve ) { void server_join_clients(struct server *serve)
{
int i; int i;
void *status; void *status;
@@ -751,7 +770,8 @@ void server_join_clients( struct server * serve ) {
if (0 == err) { if (0 == err) {
serve->nbd_client[i].thread = 0; serve->nbd_client[i].thread = 0;
} else { } else {
warn( "Error %s (%i) joining thread %p", strerror( err ), err, thread_id ); warn("Error %s (%i) joining thread %p", strerror(err), err,
thread_id);
} }
} }
} }
@@ -792,8 +812,6 @@ void server_control_arrived( struct server *serve )
} }
void flexnbd_stop_control( struct flexnbd * flexnbd );
/** Closes sockets, frees memory and waits for all client threads to finish */ /** Closes sockets, frees memory and waits for all client threads to finish */
void serve_cleanup(struct server *params, void serve_cleanup(struct server *params,
int fatal __attribute__ ((unused))) int fatal __attribute__ ((unused)))
@@ -803,7 +821,21 @@ void serve_cleanup(struct server* params,
info("cleaning up"); info("cleaning up");
if (params->server_fd){ close(params->server_fd); } /* Close the control socket, and wait for it to close before proceeding.
* If we do not wait, we risk a race condition with the tail supervisor
* sending a status command, and deadlocking the mirroring. */
if (params->flexnbd && params->flexnbd->control) {
debug("closing control socket");
control_signal_close(params->flexnbd->control);
debug("waiting for control socket to close");
control_wait_for_close(params->flexnbd->control);
}
if (params->server_fd) {
debug("closing server_fd");
close(params->server_fd);
}
/* need to stop background build if we're killed very early on */ /* need to stop background build if we're killed very early on */
pthread_cancel(params->allocation_map_builder_thread); pthread_cancel(params->allocation_map_builder_thread);
@@ -812,14 +844,18 @@ void serve_cleanup(struct server* params,
int need_mirror_lock; int need_mirror_lock;
need_mirror_lock = !server_start_mirror_locked(params); need_mirror_lock = !server_start_mirror_locked(params);
if ( need_mirror_lock ) { server_lock_start_mirror( params ); } if (need_mirror_lock) {
server_lock_start_mirror(params);
}
{ {
if (server_is_mirroring(params)) { if (server_is_mirroring(params)) {
server_abandon_mirror(params); server_abandon_mirror(params);
} }
server_prevent_mirror_start(params); server_prevent_mirror_start(params);
} }
if ( need_mirror_lock ) { server_unlock_start_mirror( params ); } if (need_mirror_lock) {
server_unlock_start_mirror(params);
}
server_join_clients(params); server_join_clients(params);
@@ -835,15 +871,6 @@ void serve_cleanup(struct server* params,
server_unlock_acl(params); server_unlock_acl(params);
} }
/* if( params->flexnbd ) { */
/* if ( params->flexnbd->control ) { */
/* flexnbd_stop_control( params->flexnbd ); */
/* } */
/* flexnbd_destroy( params->flexnbd ); */
/* } */
/* server_destroy( params ); */
debug("Cleanup done"); debug("Cleanup done");
} }
@@ -864,8 +891,11 @@ uint64_t server_mirror_bytes_remaining( struct server * serve )
{ {
if (server_is_mirroring(serve)) { if (server_is_mirroring(serve)) {
uint64_t bytes_to_xfer = uint64_t bytes_to_xfer =
bitset_stream_queued_bytes( serve->allocation_map, BITSET_STREAM_SET ) + bitset_stream_queued_bytes(serve->allocation_map,
( serve->size - serve->mirror->offset ); BITSET_STREAM_SET) + (serve->size -
serve->
mirror->
offset);
return bytes_to_xfer; return bytes_to_xfer;
} }
@@ -911,10 +941,8 @@ void server_abandon_mirror( struct server * serve )
* We can set abandon_signal after mirror_super has checked it, but * We can set abandon_signal after mirror_super has checked it, but
* before the reset. However, mirror_reset doesn't clear abandon_signal * before the reset. However, mirror_reset doesn't clear abandon_signal
* so it'll just terminate early on the next pass. */ * so it'll just terminate early on the next pass. */
ERROR_UNLESS( ERROR_UNLESS(self_pipe_signal(serve->mirror->abandon_signal),
self_pipe_signal( serve->mirror->abandon_signal ), "Failed to signal abandon to mirror");
"Failed to signal abandon to mirror"
);
pthread_t tid = serve->mirror_super->thread; pthread_t tid = serve->mirror_super->thread;
pthread_join(tid, NULL); pthread_join(tid, NULL);
@@ -948,7 +976,9 @@ int do_serve( struct server* params, struct self_pipe * open_signal )
/* Only signal that we are open for business once the server /* Only signal that we are open for business once the server
socket is open */ socket is open */
if ( NULL != open_signal ) { self_pipe_signal( open_signal ); } if (NULL != open_signal) {
self_pipe_signal(open_signal);
}
serve_init_allocation_map(params); serve_init_allocation_map(params);
serve_accept_loop(params); serve_accept_loop(params);

View File

@@ -103,8 +103,7 @@ struct server {
int success; int success;
}; };
struct server * server_create( struct server *server_create(struct flexnbd *flexnbd,
struct flexnbd * flexnbd,
char *s_ip_address, char *s_ip_address,
char *s_port, char *s_port,
char *s_file, char *s_file,
@@ -112,8 +111,7 @@ struct server * server_create(
int acl_entries, int acl_entries,
char **s_acl_entries, char **s_acl_entries,
int max_nbd_clients, int max_nbd_clients,
int use_killswitch, int use_killswitch, int success);
int success );
void server_destroy(struct server *); void server_destroy(struct server *);
int server_is_closed(struct server *serve); int server_is_closed(struct server *serve);
void serve_signal_close(struct server *serve); void serve_signal_close(struct server *serve);
@@ -167,4 +165,3 @@ struct mode_readwrite_params {
#endif #endif

View File

@@ -21,17 +21,20 @@ struct status * status_create( struct server * serve )
if (status->is_mirroring) { if (status->is_mirroring) {
status->migration_duration = monotonic_time_ms(); status->migration_duration = monotonic_time_ms();
if ( ( serve->mirror->migration_started ) < status->migration_duration ) { if ((serve->mirror->migration_started) <
status->migration_duration) {
status->migration_duration -= serve->mirror->migration_started; status->migration_duration -= serve->mirror->migration_started;
} else { } else {
status->migration_duration = 0; status->migration_duration = 0;
} }
status->migration_duration /= 1000; status->migration_duration /= 1000;
status->migration_speed = server_mirror_bps(serve); status->migration_speed = server_mirror_bps(serve);
status->migration_speed_limit = serve->mirror->max_bytes_per_second; status->migration_speed_limit =
serve->mirror->max_bytes_per_second;
status->migration_seconds_left = server_mirror_eta(serve); status->migration_seconds_left = server_mirror_eta(serve);
status->migration_bytes_left = server_mirror_bytes_remaining( serve ); status->migration_bytes_left =
server_mirror_bytes_remaining(serve);
} }
server_unlock_start_mirror(serve); server_unlock_start_mirror(serve);
@@ -77,4 +80,3 @@ void status_destroy( struct status * status )
NULLCHECK(status); NULLCHECK(status);
free(status); free(status);
} }

View File

@@ -101,4 +101,3 @@ void status_destroy( struct status * );
#endif #endif

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
require 'flexnbd' require 'flexnbd'
require 'file_writer' require 'file_writer'
@@ -9,18 +7,23 @@ class Environment
def initialize def initialize
@blocksize = 1024 @blocksize = 1024
@filename1 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.1" @filename1 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.1"
@filename2 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.2" @filename2 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.2"
@ip = "127.0.0.1" @ip = '127.0.0.1'
@available_ports = [*40000..41000] - listening_ports @available_ports = [*40_000..41_000] - listening_ports
@port1 = @available_ports.shift @port1 = @available_ports.shift
@port2 = @available_ports.shift @port2 = @available_ports.shift
@nbd1 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port1) @nbd1 = FlexNBD::FlexNBD.new('../../build/flexnbd', @ip, @port1)
@nbd2 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port2) @nbd2 = FlexNBD::FlexNBD.new('../../build/flexnbd', @ip, @port2)
@fake_pid = nil @fake_pid = nil
end end
def blocksize=(b)
raise RuntimeError, "Unable to change blocksize after files have been opened" if @file1 or @file2
@blocksize = b
end
def prefetch_proxy! def prefetch_proxy!
@nbd1.prefetch_proxy = true @nbd1.prefetch_proxy = true
@nbd2.prefetch_proxy = true @nbd2.prefetch_proxy = true
@@ -29,11 +32,11 @@ class Environment
def proxy1(port = @port2) def proxy1(port = @port2)
@nbd1.proxy(@ip, port) @nbd1.proxy(@ip, port)
end end
def proxy2(port = @port1) def proxy2(port = @port1)
@nbd2.proxy(@ip, port) @nbd2.proxy(@ip, port)
end end
def serve1(*acl) def serve1(*acl)
@nbd1.serve(@filename1, *acl) @nbd1.serve(@filename1, *acl)
end end
@@ -42,7 +45,6 @@ class Environment
@nbd2.serve(@filename2, *acl) @nbd2.serve(@filename2, *acl)
end end
def listen1(*acl) def listen1(*acl)
@nbd1.listen(@filename1, *(acl.empty? ? @acl1 : acl)) @nbd1.listen(@filename1, *(acl.empty? ? @acl1 : acl))
end end
@@ -51,7 +53,6 @@ class Environment
@nbd2.listen(@filename2, *acl) @nbd2.listen(@filename2, *acl)
end end
def break1 def break1
@nbd1.break @nbd1.break
end end
@@ -64,7 +65,6 @@ class Environment
@nbd2.acl(*acl) @nbd2.acl(*acl)
end end
def status1 def status1
@nbd1.status.first @nbd1.status.first
end end
@@ -73,8 +73,6 @@ class Environment
@nbd2.status.first @nbd2.status.first
end end
def mirror12 def mirror12
@nbd1.mirror(@nbd2.ip, @nbd2.port) @nbd1.mirror(@nbd2.ip, @nbd2.port)
end end
@@ -87,7 +85,6 @@ class Environment
@nbd1.mirror_unlink(@nbd2.ip, @nbd2.port, 2) @nbd1.mirror_unlink(@nbd2.ip, @nbd2.port, 2)
end end
def write1(data) def write1(data)
@nbd1.write(0, data) @nbd1.write(0, data)
end end
@@ -100,20 +97,17 @@ class Environment
@file2 = FileWriter.new(@filename2, @blocksize).write(data) @file2 = FileWriter.new(@filename2, @blocksize).write(data)
end end
def truncate1(size) def truncate1(size)
system "truncate -s #{size} #{@filename1}" system "truncate -s #{size} #{@filename1}"
end end
def listening_ports def listening_ports
`netstat -ltn`. `netstat -ltn`
split("\n"). .split("\n")
map { |x| x.split(/\s+/) }[2..-1]. .map { |x| x.split(/\s+/) }[2..-1]
map { |l| l[3].split(":")[-1].to_i } .map { |l| l[3].split(':')[-1].to_i }
end end
def cleanup def cleanup
if @fake_pid if @fake_pid
begin begin
@@ -122,41 +116,35 @@ class Environment
end end
end end
@nbd1.can_die(0) @nbd1.can_die(0)
@nbd1.kill @nbd1.kill
@nbd2.kill @nbd2.kill
[@filename1, @filename2].each do |f| [@filename1, @filename2].each do |f|
File.unlink(f) if File.exists?(f) File.unlink(f) if File.exist?(f)
end end
end end
def run_fake(name, addr, port, sock = nil) def run_fake(name, addr, port, sock = nil)
fakedir = File.join( File.dirname( __FILE__ ), "fakes" ) fakedir = File.join(File.dirname(__FILE__), 'fakes')
fakeglob = File.join( fakedir, name ) + "*" fakeglob = File.join(fakedir, name) + '*'
fake = Dir[fakeglob].sort.find { |fn| fake = Dir[fakeglob].sort.find do |fn|
File.executable?(fn) File.executable?(fn)
} end
raise "no fake executable at #{fakeglob}" unless fake raise "no fake executable at #{fakeglob}" unless fake
raise "no addr" unless addr raise 'no addr' unless addr
raise "no port" unless port raise 'no port' unless port
@fake_pid = fork do @fake_pid = fork do
exec [fake, addr, port, @nbd1.pid, sock].map{|x| x.to_s}.join(" ") exec [fake, addr, port, @nbd1.pid, sock].map(&:to_s).join(' ')
end end
sleep(0.5) sleep(0.5)
end end
def fake_reports_success def fake_reports_success
_, status = Process.waitpid2(@fake_pid) _, status = Process.waitpid2(@fake_pid)
@fake_pid = nil @fake_pid = nil
status.success? status.success?
end end
end # class Environment end # class Environment

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a server, accept a client, then cancel the migration by issuing # Open a server, accept a client, then cancel the migration by issuing
# a break command. # a break command.
@@ -13,23 +11,22 @@ client = server.accept
ctrl = UNIXSocket.open(sock) ctrl = UNIXSocket.open(sock)
Process.kill("STOP", src_pid.to_i) Process.kill('STOP', src_pid.to_i)
ctrl.write("break\n") ctrl.write("break\n")
ctrl.close_write ctrl.close_write
client.write_hello client.write_hello
Process.kill("CONT", src_pid.to_i) Process.kill('CONT', src_pid.to_i)
fail "Unexpected control response" unless raise 'Unexpected control response' unless
ctrl.read =~ /0: mirror stopped/ ctrl.read =~ /0: mirror stopped/
client2 = nil client2 = nil
begin begin
client2 = server.accept( "Expected timeout" ) client2 = server.accept('Expected timeout')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
client.close client.close
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Receive a mirror, and disconnect after sending the entrust reply but # Receive a mirror, and disconnect after sending the entrust reply but
# before it can send the disconnect signal. # before it can send the disconnect signal.
# #
@@ -15,7 +13,7 @@ server = FakeDest.new( addr, port )
client = server.accept client = server.accept
client.write_hello client.write_hello
while (req = client.read_request; req[:type] == 1) while req = client.read_request; req[:type] == 1
client.read_data(req[:len]) client.read_data(req[:len])
client.write_reply(req[:handle]) client.write_reply(req[:handle])
end end
@@ -26,11 +24,10 @@ client.close
system "kill -CONT #{src_pid}" system "kill -CONT #{src_pid}"
sleep(0.25) sleep(0.25)
client2 = server.accept( "Timed out waiting for a reconnection" ) client2 = server.accept('Timed out waiting for a reconnection')
client2.close client2.close
server.close server.close
$stderr.puts "done" warn 'done'
exit(0) exit(0)

View File

@@ -11,11 +11,11 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new(addr, port) server = FakeDest.new(addr, port)
client = server.accept( "Timed out waiting for a connection" ) client = server.accept('Timed out waiting for a connection')
client.write_hello client.write_hello
client.close client.close
new_client = server.accept( "Timed out waiting for a reconnection" ) new_client = server.accept('Timed out waiting for a reconnection')
new_client.close new_client.close
server.close server.close

View File

@@ -12,12 +12,12 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new(addr, port) server = FakeDest.new(addr, port)
client = server.accept( "Timed out waiting for a connection" ) client = server.accept('Timed out waiting for a connection')
client.write_hello client.write_hello
client.read_request client.read_request
client.close client.close
new_client = server.accept( "Timed out waiting for a reconnection" ) new_client = server.accept('Timed out waiting for a reconnection')
new_client.close new_client.close
server.close server.close

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a server, accept a client, then we expect a single write # Open a server, accept a client, then we expect a single write
# followed by an entrust. However, we disconnect after the write so # followed by an entrust. However, we disconnect after the write so
# the entrust will fail. We don't expect a reconnection: the sender # the entrust will fail. We don't expect a reconnection: the sender
@@ -17,9 +15,9 @@ client.write_hello
req = client.read_request req = client.read_request
data = client.read_data(req[:len]) data = client.read_data(req[:len])
Process.kill("STOP", src_pid.to_i) Process.kill('STOP', src_pid.to_i)
client.write_reply(req[:handle], 0) client.write_reply(req[:handle], 0)
client.close client.close
Process.kill("CONT", src_pid.to_i) Process.kill('CONT', src_pid.to_i)
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
require 'flexnbd/fake_dest' require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
@@ -12,8 +10,7 @@ client.write_hello
handle = client.read_request[:handle] handle = client.read_request[:handle]
client.write_error(handle) client.write_error(handle)
client2 = server.accept('Timed out waiting for a reconnection')
client2 = server.accept( "Timed out waiting for a reconnection" )
client.close client.close
client2.close client2.close

View File

@@ -26,8 +26,8 @@ client.close
# Invert the sense of the timeout exception, since we *don't* want a # Invert the sense of the timeout exception, since we *don't* want a
# connection attempt # connection attempt
begin begin
server.accept( "Expected timeout" ) server.accept('Expected timeout')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a socket, say hello, receive a write, then sleep for > # Open a socket, say hello, receive a write, then sleep for >
# MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the # MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the
# write has gone MIA, and we expect a reconnect. # write has gone MIA, and we expect a reconnect.
@@ -15,12 +13,12 @@ client1.write_hello
client1.read_request client1.read_request
t = Thread.start do t = Thread.start do
client2 = server.accept( "Timed out waiting for a reconnection", client2 = server.accept('Timed out waiting for a reconnection',
FlexNBD::MS_REQUEST_LIMIT_SECS + 2) FlexNBD::MS_REQUEST_LIMIT_SECS + 2)
client2.close client2.close
end end
sleep_time = if ENV.has_key?('FLEXNBD_MS_REQUEST_LIMIT_SECS') sleep_time = if ENV.key?('FLEXNBD_MS_REQUEST_LIMIT_SECS')
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f
else else
FlexNBD::MS_REQUEST_LIMIT_SECS FlexNBD::MS_REQUEST_LIMIT_SECS

View File

@@ -13,15 +13,15 @@ client1 = server.accept
# We don't expect a reconnection attempt. # We don't expect a reconnection attempt.
t = Thread.new do t = Thread.new do
begin begin
client2 = server.accept( "Timed out waiting for a reconnection", client2 = server.accept('Timed out waiting for a reconnection',
FlexNBD::MS_RETRY_DELAY_SECS + 1) FlexNBD::MS_RETRY_DELAY_SECS + 1)
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
end end
client1.write_hello( :magic => :wrong ) client1.write_hello(magic: :wrong)
t.join t.join

View File

@@ -18,21 +18,21 @@ t = Thread.new do
# so it makes no sense to continue. This means we have to invert the # so it makes no sense to continue. This means we have to invert the
# sense of the exception. # sense of the exception.
begin begin
client2 = server.accept( "Timed out waiting for a reconnection", client2 = server.accept('Timed out waiting for a reconnection',
FlexNBD::MS_RETRY_DELAY_SECS + 1) FlexNBD::MS_RETRY_DELAY_SECS + 1)
client2.close client2.close
fail "Unexpected reconnection." raise 'Unexpected reconnection.'
rescue Timeout::Error rescue Timeout::Error
end end
end end
client.write_hello( :size => :wrong ) client.write_hello(size: :wrong)
t.join t.join
# Now check that the source closed the first socket (yes, this was an # Now check that the source closed the first socket (yes, this was an
# actual bug) # actual bug)
fail "Didn't close socket" unless client.disconnected? raise "Didn't close socket" unless client.disconnected?
exit 0 exit 0

View File

@@ -10,15 +10,13 @@ addr, port = *ARGV
server = FakeDest.new(addr, port) server = FakeDest.new(addr, port)
server.accept.close server.accept.close
begin begin
server.accept server.accept
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
server.close server.close
exit(0) exit(0)

View File

@@ -9,7 +9,7 @@ include FlexNBD
addr, port, pid = *ARGV addr, port, pid = *ARGV
server = FakeDest.new(addr, port) server = FakeDest.new(addr, port)
client = server.accept( "Timed out waiting for a connection" ) client = server.accept('Timed out waiting for a connection')
client.write_hello client.write_hello
Process.kill(15, pid.to_i) Process.kill(15, pid.to_i)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Accept a connection, write hello, wait for a write request, read the # Accept a connection, write hello, wait for a write request, read the
# data, then write back a reply with a bad magic field. We then # data, then write back a reply with a bad magic field. We then
# expect a reconnect. # expect a reconnect.
@@ -15,7 +13,7 @@ client = server.accept
client.write_hello client.write_hello
req = client.read_request req = client.read_request
client.read_data(req[:len]) client.read_data(req[:len])
client.write_reply( req[:handle], 0, :magic => :wrong ) client.write_reply(req[:handle], 0, magic: :wrong)
client2 = server.accept client2 = server.accept
client.close client.close

View File

@@ -11,13 +11,13 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
FakeSource.new( addr, port, "Failed to connect" ).close FakeSource.new(addr, port, 'Failed to connect').close
# Sleep to be sure we don't try to connect too soon. That wouldn't # Sleep to be sure we don't try to connect too soon. That wouldn't
# be a problem for the destination, but it would prevent us from # be a problem for the destination, but it would prevent us from
# determining success or failure here in the case where we try to # determining success or failure here in the case where we try to
# reconnect before the destination has tidied up after the first # reconnect before the destination has tidied up after the first
# thread went away. # thread went away.
sleep(0.5) sleep(0.5)
FakeSource.new( addr, port, "Failed to reconnect" ).close FakeSource.new(addr, port, 'Failed to reconnect').close
exit 0 exit 0

View File

@@ -11,10 +11,10 @@ include FlexNBD
addr, port, srv_pid = *ARGV addr, port, srv_pid = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.read_hello client.read_hello
client.write_write_request(0, 8) client.write_write_request(0, 8)
client.write_data( "12345678" ) client.write_data('12345678')
# Use system "kill" rather than Process.kill because Process.kill # Use system "kill" rather than Process.kill because Process.kill
# doesn't seem to work # doesn't seem to work
@@ -25,12 +25,11 @@ client.close
system "kill -CONT #{srv_pid}" system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
begin begin
client2 = FakeSource.new( addr, port, "Expected timeout" ) client2 = FakeSource.new(addr, port, 'Expected timeout')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end

View File

@@ -10,10 +10,10 @@ include FlexNBD
addr, port, srv_pid = *ARGV addr, port, srv_pid = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.read_hello client.read_hello
client.write_write_request(0, 8) client.write_write_request(0, 8)
client.write_data( "12345678" ) client.write_data('12345678')
client.write_entrust_request client.write_entrust_request
client.read_response client.read_response
@@ -21,13 +21,11 @@ client.close
sleep(0.25) sleep(0.25)
begin begin
client2 = FakeSource.new( addr, port, "Expected timeout" ) client2 = FakeSource.new(addr, port, 'Expected timeout')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
exit(0) exit(0)

View File

@@ -12,13 +12,12 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting.')
client = FakeSource.new( addr, port, "Timed out connecting." )
client.read_hello client.read_hello
client.close client.close
sleep(0.2) sleep(0.2)
FakeSource.new( addr, port, "Timed out reconnecting." ) FakeSource.new(addr, port, 'Timed out reconnecting.')
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect, pause the server, issue a write request, disconnect, # We connect, pause the server, issue a write request, disconnect,
# then cont the server. This ensures that our disconnect happens # then cont the server. This ensures that our disconnect happens
# while the server is trying to read the write data. # while the server is trying to read the write data.
@@ -10,7 +8,7 @@ include FlexNBD
addr, port, srv_pid = *ARGV addr, port, srv_pid = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.read_hello client.read_hello
system "kill -STOP #{srv_pid}" system "kill -STOP #{srv_pid}"
@@ -24,7 +22,7 @@ system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
# ...and can we reconnect? # ...and can we reconnect?
client2 = FakeSource.new( addr, port, "Timed out connecting" ) client2 = FakeSource.new(addr, port, 'Timed out connecting')
client2.close client2.close
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect, pause the server, issue a write request, send data, # We connect, pause the server, issue a write request, send data,
# disconnect, then cont the server. This ensures that our disconnect # disconnect, then cont the server. This ensures that our disconnect
# happens before the server can try to write the reply. # happens before the server can try to write the reply.
@@ -10,13 +8,13 @@ include FlexNBD
addr, port, srv_pid = *ARGV addr, port, srv_pid = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.read_hello client.read_hello
system "kill -STOP #{srv_pid}" system "kill -STOP #{srv_pid}"
client.write_write_request(0, 8) client.write_write_request(0, 8)
client.write_data( "12345678" ) client.write_data('12345678')
client.close client.close
system "kill -CONT #{srv_pid}" system "kill -CONT #{srv_pid}"
@@ -27,7 +25,7 @@ system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
# ...and can we reconnect? # ...and can we reconnect?
client2 = FakeSource.new( addr, port, "Timed out reconnecting" ) client2 = FakeSource.new(addr, port, 'Timed out reconnecting')
client2.close client2.close
exit(0) exit(0)

View File

@@ -8,10 +8,9 @@ include FlexNBD
addr, port, srv_pid, newaddr, newport = *ARGV addr, port, srv_pid, newaddr, newport = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.write_read_request(0, 8) client.write_read_request(0, 8)
client.read_raw(4) client.read_raw(4)
client.close client.close
exit(0) exit(0)

View File

@@ -10,9 +10,9 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client1 = FakeSource.new( addr, port, "Timed out connecting" ) client1 = FakeSource.new(addr, port, 'Timed out connecting')
sleep(0.25) sleep(0.25)
client2 = FakeSource.new( addr, port, "Timed out connecting a second time" ) client2 = FakeSource.new(addr, port, 'Timed out connecting a second time')
# This is the expected source crashing after connect # This is the expected source crashing after connect
client1.close client1.close

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect from a local address which should be blocked, sleep for a # We connect from a local address which should be blocked, sleep for a
# bit, then try to read from the socket. We should get an instant EOF # bit, then try to read from the socket. We should get an instant EOF
# as we've been cut off by the destination. # as we've been cut off by the destination.
@@ -11,10 +9,9 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting", "127.0.0.6" ) client = FakeSource.new(addr, port, 'Timed out connecting', '127.0.0.6')
sleep(0.25) sleep(0.25)
rsp = client.disconnected? ? 0 : 1 rsp = client.disconnected? ? 0 : 1
client.close client.close
exit(rsp) exit(rsp)

View File

@@ -7,10 +7,10 @@
# listening for an incoming migration. # listening for an incoming migration.
addr, port = *ARGV addr, port = *ARGV
require "flexnbd/fake_source" require 'flexnbd/fake_source'
include FlexNBD include FlexNBD
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.read_hello client.read_hello
# Now we do two things: # Now we do two things:
@@ -25,7 +25,7 @@ kidpid = fork do
client.close client.close
new_client = nil new_client = nil
sleep(FlexNBD::CLIENT_MAX_WAIT_SECS + 1) sleep(FlexNBD::CLIENT_MAX_WAIT_SECS + 1)
new_client = FakeSource.new( addr, port, "Timed out reconnecting." ) new_client = FakeSource.new(addr, port, 'Timed out reconnecting.')
new_client.read_hello new_client.read_hello
exit 0 exit 0
end end

View File

@@ -9,10 +9,10 @@ include FlexNBD
addr, port, pid = *ARGV addr, port, pid = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting." ) client = FakeSource.new(addr, port, 'Timed out connecting.')
client.read_hello client.read_hello
Process.kill( "TERM", pid.to_i ) Process.kill('TERM', pid.to_i)
sleep(0.2) sleep(0.2)
client.close client.close

View File

@@ -9,10 +9,9 @@ include FlexNBD
addr, port, srv_pid, newaddr, newport = *ARGV addr, port, srv_pid, newaddr, newport = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
client.send_mirror() client.send_mirror
sleep(1) sleep(1)
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Connect, read the hello then make a write request with an impossible # Connect, read the hello then make a write request with an impossible
# (from,len) pair. We expect an error response, and not to be # (from,len) pair. We expect an error response, and not to be
# disconnected. # disconnected.
@@ -13,20 +11,20 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new( addr, port, "Timed out connecting" ) client = FakeSource.new(addr, port, 'Timed out connecting')
hello = client.read_hello hello = client.read_hello
client.write_write_request( hello[:size]+1, 32, "myhandle" ) client.write_write_request(hello[:size] + 1, 32, 'myhandle')
client.write_data("1"*32) client.write_data('1' * 32)
response = client.read_response response = client.read_response
fail "Not an error" if response[:error] == 0 raise 'Not an error' if response[:error] == 0
fail "Wrong handle" unless "myhandle" == response[:handle] raise 'Wrong handle' unless response[:handle] == 'myhandle'
client.write_write_request(0, 32) client.write_write_request(0, 32)
client.write_data( "2"*32 ) client.write_data('2' * 32)
success_response = client.read_response success_response = client.read_response
fail "Second write failed" unless success_response[:error] == 0 raise 'Second write failed' unless success_response[:error] == 0
client.close client.close
exit(0) exit(0)

View File

@@ -3,13 +3,13 @@
# #
class FileWriter class FileWriter
def initialize(filename, blocksize) def initialize(filename, blocksize)
@fh = File.open(filename, "w+") @fh = File.open(filename, 'w+')
@blocksize = blocksize @blocksize = blocksize
@pattern = "" @pattern = ''
end end
def size def size
@blocksize * @pattern.split("").size @blocksize * @pattern.split('').size
end end
# We write in fixed block sizes, given by "blocksize" # We write in fixed block sizes, given by "blocksize"
@@ -20,8 +20,8 @@ class FileWriter
def write(data) def write(data)
@pattern += data @pattern += data
data.split("").each do |code| data.split('').each do |code|
if code == "_" if code == '_'
@fh.seek(@blocksize, IO::SEEK_CUR) @fh.seek(@blocksize, IO::SEEK_CUR)
else else
@fh.write(data(code)) @fh.write(data(code))
@@ -31,15 +31,14 @@ class FileWriter
self self
end end
# Returns what the data ought to be at the given offset and length # Returns what the data ought to be at the given offset and length
# #
def read_original(off, len) def read_original(off, len)
patterns = @pattern.split( "" ) patterns = @pattern.split('')
patterns.zip( (0...patterns.length).to_a ). patterns.zip((0...patterns.length).to_a)
map { |blk, blk_off| .map do |blk, blk_off|
data(blk, blk_off) data(blk, blk_off)
}.join[off...(off+len)] end.join[off...(off + len)]
end end
# Read what's actually in the file # Read what's actually in the file
@@ -62,14 +61,14 @@ class FileWriter
def data(code, at = @fh.tell) def data(code, at = @fh.tell)
case code case code
when "0", "_" when '0', '_'
"\0" * @blocksize "\0" * @blocksize
when "X" when 'X'
"X" * @blocksize 'X' * @blocksize
when "f" when 'f'
r = "" r = ''
(@blocksize / 4).times do (@blocksize / 4).times do
r += [at].pack("I") r += [at].pack('I')
at += 4 at += 4
end end
r r
@@ -77,51 +76,49 @@ class FileWriter
raise "Unknown character '#{block}'" raise "Unknown character '#{block}'"
end end
end end
end end
if __FILE__==$0 if $PROGRAM_NAME == __FILE__
require 'tempfile' require 'tempfile'
require 'test/unit' require 'test/unit'
class FileWriterTest < Test::Unit::TestCase class FileWriterTest < Test::Unit::TestCase
def test_read_original_zeros def test_read_original_zeros
Tempfile.open("test_read_original_zeros") do |tempfile| Tempfile.open('test_read_original_zeros') do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new(tempfile.path, 4096)
file.write( "0" ) file.write('0')
assert_equal file.read(0, 4096), file.read_original(0, 4096) assert_equal file.read(0, 4096), file.read_original(0, 4096)
assert( file.untouched?(0,4096) , "Untouched file was touched." ) assert(file.untouched?(0, 4096), 'Untouched file was touched.')
end end
end end
def test_read_original_offsets def test_read_original_offsets
Tempfile.open("test_read_original_offsets") do |tempfile| Tempfile.open('test_read_original_offsets') do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new(tempfile.path, 4096)
file.write( "f" ) file.write('f')
assert_equal file.read(0, 4096), file.read_original(0, 4096) assert_equal file.read(0, 4096), file.read_original(0, 4096)
assert( file.untouched?(0,4096) , "Untouched file was touched." ) assert(file.untouched?(0, 4096), 'Untouched file was touched.')
end end
end end
def test_file_size def test_file_size
Tempfile.open("test_file_size") do |tempfile| Tempfile.open('test_file_size') do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new(tempfile.path, 4096)
file.write( "f" ) file.write('f')
assert_equal 4096, File.stat(tempfile.path).size assert_equal 4096, File.stat(tempfile.path).size
end end
end end
def test_read_original_size def test_read_original_size
Tempfile.open("test_read_original_offsets") do |tempfile| Tempfile.open('test_read_original_offsets') do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4) file = FileWriter.new(tempfile.path, 4)
file.write( "f"*4 ) file.write('f' * 4)
assert_equal 4, file.read_original(0, 4).length assert_equal 4, file.read_original(0, 4).length
end end
end end
end end
end end

View File

@@ -4,28 +4,26 @@ require 'open3'
require 'timeout' require 'timeout'
require 'rexml/document' require 'rexml/document'
require 'rexml/streamlistener' require 'rexml/streamlistener'
require 'English'
Thread.abort_on_exception = true Thread.abort_on_exception = true
class Executor class Executor
attr_reader :pid attr_reader :pid
def run(cmd) def run(cmd)
@pid = fork do exec cmd end @pid = fork { exec cmd }
end end
end # class Executor end # class Executor
class ValgrindExecutor class ValgrindExecutor
attr_reader :pid attr_reader :pid
def run(cmd) def run(cmd)
@pid = fork do exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" end @pid = fork { exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" }
end end
end # class ValgrindExecutor end # class ValgrindExecutor
class ValgrindKillingExecutor class ValgrindKillingExecutor
attr_reader :pid attr_reader :pid
@@ -34,9 +32,9 @@ class ValgrindKillingExecutor
attr_reader :backtrace attr_reader :backtrace
def initialize def initialize
@backtrace = [] @backtrace = []
@what = "" @what = ''
@kind = "" @kind = ''
@pid = "" @pid = ''
end end
def add_frame def add_frame
@@ -58,10 +56,8 @@ class ValgrindKillingExecutor
def to_s def to_s
([@what + " (#{@kind}) in #{@pid}"] + @backtrace.map { |h| "#{h[:file]}:#{h[:line]} #{h[:fn]}" }).join("\n") ([@what + " (#{@kind}) in #{@pid}"] + @backtrace.map { |h| "#{h[:file]}:#{h[:line]} #{h[:fn]}" }).join("\n")
end end
end # class Error end # class Error
class ErrorListener class ErrorListener
include REXML::StreamListener include REXML::StreamListener
def initialize(killer) def initialize(killer)
@@ -74,39 +70,36 @@ class ValgrindKillingExecutor
@text = text @text = text
end end
def tag_start(tag, attrs) def tag_start(tag, _attrs)
case tag.to_s case tag.to_s
when "error" when 'error'
@found = true @found = true
when "frame" when 'frame'
@error.add_frame @error.add_frame
end end
end end
def tag_end(tag) def tag_end(tag)
case tag.to_s case tag.to_s
when "what" when 'what'
@error.what = @text if @found @error.what = @text if @found
@text = "" @text = ''
when "kind" when 'kind'
@error.kind = @text if @found @error.kind = @text if @found
when "file" when 'file'
@error.add_file(@text) if @found @error.add_file(@text) if @found
when "fn" when 'fn'
@error.add_fn(@text) if @found @error.add_fn(@text) if @found
when "line" when 'line'
@error.add_line(@text) if @found @error.add_line(@text) if @found
when "error", "stack" when 'error', 'stack'
if @found @killer.call(@error) if @found
@killer.call( @error ) when 'pid'
end
when "pid"
@error.pid = @text @error.pid = @text
end end
end end
end # class ErrorListener end # class ErrorListener
class DebugErrorListener < ErrorListener class DebugErrorListener < ErrorListener
def text(txt) def text(txt)
print txt print txt
@@ -124,47 +117,41 @@ class ValgrindKillingExecutor
end end
end end
def initialize def initialize
@pid = nil @pid = nil
end end
def run(cmd) def run(cmd)
@io_r, io_w = IO.pipe @io_r, io_w = IO.pipe
@pid = fork do exec( "valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd ) end @pid = fork { exec("valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd) }
launch_watch_thread(@pid, @io_r) launch_watch_thread(@pid, @io_r)
@pid @pid
end end
def call(err) def call(err)
$stderr.puts "*"*72 warn '*' * 72
$stderr.puts "* Valgrind error spotted:" warn '* Valgrind error spotted:'
$stderr.puts err.to_s.split("\n").map{|s| " #{s}"} warn err.to_s.split("\n").map { |s| " #{s}" }
$stderr.puts "*"*72 warn '*' * 72
Process.kill( "KILL", @pid ) Process.kill('KILL', @pid)
exit(1) exit(1)
end end
private private
def pick_listener def pick_listener
ENV['DEBUG'] ? DebugErrorListener : ErrorListener ENV['DEBUG'] ? DebugErrorListener : ErrorListener
end end
def launch_watch_thread(pid, io_r) def launch_watch_thread(_pid, io_r)
Thread.start do Thread.start do
io_source = REXML::IOSource.new(io_r) io_source = REXML::IOSource.new(io_r)
listener = pick_listener.new(self) listener = pick_listener.new(self)
REXML::Document.parse_stream(io_source, listener) REXML::Document.parse_stream(io_source, listener)
end end
end end
end # class ValgrindExecutor end # class ValgrindExecutor
module FlexNBD module FlexNBD
# Noddy test class to exercise FlexNBD from the outside for testing. # Noddy test class to exercise FlexNBD from the outside for testing.
# #
@@ -189,12 +176,11 @@ module FlexNBD
end end
end end
def build_debug_opt def build_debug_opt
if @do_debug if @do_debug
"--verbose" '--verbose'
else else
"--quiet" '--quiet'
end end
end end
@@ -209,20 +195,19 @@ module FlexNBD
@ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}" @ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}"
@ip = ip @ip = ip
@port = port @port = port
@pid = @wait_thread = nil
@kill = [] @kill = []
@prefetch_proxy = false @prefetch_proxy = false
end end
def debug? def debug?
!!@do_debug !!@do_debug
end end
def debug(msg) def debug(msg)
$stderr.puts msg if debug? warn msg if debug?
end end
def serve_cmd(file, acl) def serve_cmd(file, acl)
"#{bin} serve "\ "#{bin} serve "\
"--addr #{ip} "\ "--addr #{ip} "\
@@ -233,7 +218,6 @@ module FlexNBD
"#{acl.join(' ')}" "#{acl.join(' ')}"
end end
def listen_cmd(file, acl) def listen_cmd(file, acl)
"#{bin} listen "\ "#{bin} listen "\
"--addr #{ip} "\ "--addr #{ip} "\
@@ -250,11 +234,10 @@ module FlexNBD
"--port #{port} "\ "--port #{port} "\
"--conn-addr #{connect_ip} "\ "--conn-addr #{connect_ip} "\
"--conn-port #{connect_port} "\ "--conn-port #{connect_port} "\
"#{prefetch_proxy ? "--cache " : ""}"\ "#{prefetch_proxy ? '--cache ' : ''}"\
"#{@debug}" "#{@debug}"
end end
def read_cmd(offset, length) def read_cmd(offset, length)
"#{bin} read "\ "#{bin} read "\
"--addr #{ip} "\ "--addr #{ip} "\
@@ -264,7 +247,6 @@ module FlexNBD
"--size #{length}" "--size #{length}"
end end
def write_cmd(offset, data) def write_cmd(offset, data)
"#{bin} write "\ "#{bin} write "\
"--addr #{ip} "\ "--addr #{ip} "\
@@ -274,7 +256,6 @@ module FlexNBD
"--size #{data.length}" "--size #{data.length}"
end end
def base_mirror_opts(dest_ip, dest_port) def base_mirror_opts(dest_ip, dest_port)
"--addr #{dest_ip} "\ "--addr #{dest_ip} "\
"--port #{dest_port} "\ "--port #{dest_port} "\
@@ -283,7 +264,7 @@ module FlexNBD
def unlink_mirror_opts(dest_ip, dest_port) def unlink_mirror_opts(dest_ip, dest_port)
"#{base_mirror_opts(dest_ip, dest_port)} "\ "#{base_mirror_opts(dest_ip, dest_port)} "\
"--unlink " '--unlink '
end end
def base_mirror_cmd(opts) def base_mirror_cmd(opts)
@@ -316,46 +297,52 @@ module FlexNBD
"#{@bin} acl " \ "#{@bin} acl " \
"--sock #{ctrl} "\ "--sock #{ctrl} "\
"#{@debug} "\ "#{@debug} "\
"#{acl.join " "}" "#{acl.join ' '}"
end end
def run_serve_cmd(cmd) def run_serve_cmd(cmd)
File.unlink(ctrl) if File.exists?(ctrl) File.unlink(ctrl) if File.exist?(ctrl)
debug(cmd) debug(cmd)
@pid = @executor.run(cmd) @pid = @executor.run(cmd)
while !File.socket?(ctrl) until File.socket?(ctrl)
pid, status = Process.wait2(@pid, Process::WNOHANG) pid, status = Process.wait2(@pid, Process::WNOHANG)
raise "server did not start (#{cmd})" if pid raise "server did not start (#{cmd})" if pid
sleep 0.1 sleep 0.1
end end
start_wait_thread(@pid) start_wait_thread(@pid)
at_exit { kill } at_exit { kill }
end end
private :run_serve_cmd private :run_serve_cmd
def serve(file, *acl) def serve(file, *acl)
cmd = serve_cmd(file, acl) cmd = serve_cmd(file, acl)
run_serve_cmd(cmd) run_serve_cmd(cmd)
sleep( 0.2 ) until File.exists?( ctrl ) sleep(0.2) until File.exist?(ctrl)
end end
def listen(file, *acl) def listen(file, *acl)
run_serve_cmd(listen_cmd(file, acl)) run_serve_cmd(listen_cmd(file, acl))
end end
def tcp_server_open? def tcp_server_open?
# raises if the other side doesn't accept() # raises if the other side doesn't accept()
sock = TCPSocket.new(ip, port) rescue nil sock = begin
TCPSocket.new(ip, port)
rescue StandardError
nil
end
success = !!sock success = !!sock
( sock.close rescue nil) if sock if sock
(begin
sock.close
rescue StandardError
nil
end)
end
success success
end end
@@ -375,27 +362,25 @@ module FlexNBD
at_exit { kill } at_exit { kill }
end end
def start_wait_thread(pid) def start_wait_thread(pid)
@wait_thread = Thread.start do @wait_thread = Thread.start do
_, status = Process.waitpid2(pid) _, status = Process.waitpid2(pid)
if @kill if @kill
if status.signaled? if status.signaled?
fail "flexnbd quit with a bad signal: #{status.inspect}" unless raise "flexnbd quit with a bad signal: #{status.inspect}" unless
@kill.include? status.termsig @kill.include? status.termsig
else else
fail "flexnbd quit with a bad status: #{status.inspect}" unless raise "flexnbd quit with a bad status: #{status.inspect}" unless
@kill.include? status.exitstatus @kill.include? status.exitstatus
end end
else else
$stderr.puts "flexnbd #{self.pid} quit" warn "flexnbd #{self.pid} quit"
fail "flexnbd #{self.pid} quit early with status #{status.to_i}" raise "flexnbd #{self.pid} quit early with status #{status.to_i}"
end end
end end
end end
def can_die(*status) def can_die(*status)
status = [0] if status.empty? status = [0] if status.empty?
@kill += status @kill += status
@@ -407,7 +392,7 @@ module FlexNBD
can_die(1) can_die(1)
if @pid if @pid
begin begin
Process.kill("INT", @pid) Process.kill('INT', @pid)
rescue Errno::ESRCH => e rescue Errno::ESRCH => e
# already dead. Presumably this means it went away after a # already dead. Presumably this means it went away after a
# can_die() call. # can_die() call.
@@ -423,7 +408,7 @@ module FlexNBD
IO.popen(cmd) do |fh| IO.popen(cmd) do |fh|
return fh.read return fh.read
end end
raise IOError.new "NBD read failed" unless $?.success? raise IOError, 'NBD read failed' unless $CHILD_STATUS.success?
out out
end end
@@ -431,27 +416,24 @@ module FlexNBD
cmd = write_cmd(offset, data) cmd = write_cmd(offset, data)
debug(cmd) debug(cmd)
IO.popen(cmd, "w") do |fh| IO.popen(cmd, 'w') do |fh|
fh.write(data) fh.write(data)
end end
raise IOError.new "NBD write failed" unless $?.success? raise IOError, 'NBD write failed' unless $CHILD_STATUS.success?
nil nil
end end
def join def join
@wait_thread.join @wait_thread.join
end end
def mirror_unchecked(dest_ip, dest_port, _bandwidth = nil, _action = nil, timeout = nil)
def mirror_unchecked( dest_ip, dest_port, bandwidth=nil, action=nil, timeout=nil )
cmd = mirror_cmd(dest_ip, dest_port) cmd = mirror_cmd(dest_ip, dest_port)
debug(cmd) debug(cmd)
maybe_timeout(cmd, timeout) maybe_timeout(cmd, timeout)
end end
def mirror_unlink(dest_ip, dest_port, timeout = nil) def mirror_unlink(dest_ip, dest_port, timeout = nil)
cmd = mirror_unlink_cmd(dest_ip, dest_port) cmd = mirror_unlink_cmd(dest_ip, dest_port)
debug(cmd) debug(cmd)
@@ -459,11 +441,11 @@ module FlexNBD
maybe_timeout(cmd, timeout) maybe_timeout(cmd, timeout)
end end
def maybe_timeout(cmd, timeout = nil) def maybe_timeout(cmd, timeout = nil)
stdout, stderr = "","" stdout = ''
stderr = ''
stat = nil stat = nil
run = Proc.new do run = proc do
# Ruby 1.9 changed the popen3 api. instead of 3 args, the block # Ruby 1.9 changed the popen3 api. instead of 3 args, the block
# gets 4. Not only that, but it no longer sets $?, so we have to # gets 4. Not only that, but it no longer sets $?, so we have to
# go elsewhere for the process' exit status. # go elsewhere for the process' exit status.
@@ -473,7 +455,7 @@ module FlexNBD
stderr.replace io_err.read stderr.replace io_err.read
stat = maybe_thr.value if maybe_thr stat = maybe_thr.value if maybe_thr
end end
stat ||= $? stat ||= $CHILD_STATUS
end end
if timeout if timeout
@@ -485,16 +467,13 @@ module FlexNBD
[stdout, stderr, stat] [stdout, stderr, stat]
end end
def mirror(dest_ip, dest_port, bandwidth = nil, action = nil) def mirror(dest_ip, dest_port, bandwidth = nil, action = nil)
stdout, stderr, status = mirror_unchecked(dest_ip, dest_port, bandwidth, action) stdout, stderr, status = mirror_unchecked(dest_ip, dest_port, bandwidth, action)
raise IOError.new( "Migrate command failed\n" + stderr) unless status.success? raise IOError, "Migrate command failed\n" + stderr unless status.success?
stdout stdout
end end
def break(timeout = nil) def break(timeout = nil)
cmd = break_cmd cmd = break_cmd
debug(cmd) debug(cmd)
@@ -502,7 +481,6 @@ module FlexNBD
maybe_timeout(cmd, timeout) maybe_timeout(cmd, timeout)
end end
def acl(*acl) def acl(*acl)
cmd = acl_cmd(*acl) cmd = acl_cmd(*acl)
debug(cmd) debug(cmd)
@@ -510,9 +488,8 @@ module FlexNBD
maybe_timeout(cmd, 2) maybe_timeout(cmd, 2)
end end
def status(timeout = nil) def status(timeout = nil)
cmd = status_cmd() cmd = status_cmd
debug(cmd) debug(cmd)
o, e = maybe_timeout(cmd, timeout) o, e = maybe_timeout(cmd, timeout)
@@ -520,50 +497,43 @@ module FlexNBD
[parse_status(o), e] [parse_status(o), e]
end end
def launched? def launched?
!!@pid !!@pid
end end
def paused def paused
Process.kill( "STOP", @pid ) Process.kill('STOP', @pid)
yield yield
ensure ensure
Process.kill( "CONT", @pid ) Process.kill('CONT', @pid)
end end
protected protected
def control_command(*args) def control_command(*args)
raise "Server not running" unless @pid raise 'Server not running' unless @pid
args = args.compact args = args.compact
UNIXSocket.open(@ctrl) do |u| UNIXSocket.open(@ctrl) do |u|
u.write(args.join("\n") + "\n") u.write(args.join("\n") + "\n")
code, message = u.readline.split(": ", 2) code, message = u.readline.split(': ', 2)
return [code, message] return [code, message]
end end
end end
def parse_status(status) def parse_status(status)
hsh = {} hsh = {}
status.split(" ").each do |part| status.split(' ').each do |part|
next if part.strip.empty? next if part.strip.empty?
a,b = part.split("=") a, b = part.split('=')
b.strip! b.strip!
b = true if b == "true" b = true if b == 'true'
b = false if b == "false" b = false if b == 'false'
hsh[a.strip] = b hsh[a.strip] = b
end end
hsh hsh
end end
end end
end end

View File

@@ -1,10 +1,7 @@
# encoding: utf-8
module FlexNBD module FlexNBD
def self.binary(str) def self.binary(str)
if str.respond_to? :force_encoding if str.respond_to? :force_encoding
str.force_encoding "ASCII-8BIT" str.force_encoding 'ASCII-8BIT'
else else
str str
end end
@@ -13,36 +10,33 @@ module FlexNBD
# eeevil is his one and only name... # eeevil is his one and only name...
def self.read_constants def self.read_constants
parents = [] parents = []
current = File.expand_path(".") current = File.expand_path('.')
while current != "/" while current != '/'
parents << current parents << current
current = File.expand_path( File.join( current, ".." ) ) current = File.expand_path(File.join(current, '..'))
end end
source_root = parents.find do |dirname| source_root = parents.find do |dirname|
File.directory?( File.join( dirname, "src" ) ) File.directory?(File.join(dirname, 'src'))
end end
fail "No source root!" unless source_root raise 'No source root!' unless source_root
headers = Dir[File.join( source_root, "src", "{common,proxy,server}","*.h" ) ] headers = Dir[File.join(source_root, 'src', '{common,proxy,server}', '*.h')]
headers.each do |header_filename| headers.each do |header_filename|
txt_lines = File.readlines(header_filename) txt_lines = File.readlines(header_filename)
txt_lines.each do |line| txt_lines.each do |line|
if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/ if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/
# Bodge until I can figure out what to do with #ifdefs # Bodge until I can figure out what to do with #ifdefs
const_set($1, $2.to_i) unless const_defined?( $1 ) const_set(Regexp.last_match(1), Regexp.last_match(2).to_i) unless const_defined?(Regexp.last_match(1))
end
end end
end end
end end
end read_constants
read_constants()
REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC) REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC)
REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC) REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC)
end # module FlexNBD end # module FlexNBD

View File

@@ -1,21 +1,18 @@
# encoding: utf-8
require 'socket' require 'socket'
require 'timeout' require 'timeout'
require 'io/wait' # For IO#nread
require 'flexnbd/constants' require 'flexnbd/constants'
module FlexNBD module FlexNBD
class FakeDest class FakeDest
class Client class Client
def initialize(sock) def initialize(sock)
@sock = sock @sock = sock
end end
def write_hello(opts = {}) def write_hello(opts = {})
@sock.write( "NBDMAGIC" ) @sock.write('NBDMAGIC')
if opts[:magic] == :wrong if opts[:magic] == :wrong
write_rand(@sock, 8) write_rand(@sock, 8)
@@ -32,13 +29,11 @@ module FlexNBD
@sock.write("\x00" * 128) @sock.write("\x00" * 128)
end end
def write_rand(sock, len) def write_rand(sock, len)
len.times do sock.write( rand(256).chr ) end len.times { sock.write(rand(256).chr) }
end end
def read_request
def read_request()
req = @sock.read(28) req = @sock.read(28)
magic_s = req[0...4] magic_s = req[0...4]
@@ -48,11 +43,11 @@ module FlexNBD
len_s = req[24...28] len_s = req[24...28]
{ {
:magic => magic_s, magic: magic_s,
:type => type_s.unpack("N").first, type: type_s.unpack('N').first,
:handle => handle_s, handle: handle_s,
:from => self.class.parse_be64( from_s ), from: self.class.parse_be64(from_s),
:len => len_s.unpack( "N").first len: len_s.unpack('N').first
} }
end end
@@ -60,15 +55,17 @@ module FlexNBD
write_reply(handle, 1) write_reply(handle, 1)
end end
def nread
@sock.nread
end
def disconnected? def disconnected?
begin
Timeout.timeout(2) do Timeout.timeout(2) do
@sock.read(1) == nil @sock.read(1).nil?
end end
rescue Timeout::Error rescue Timeout::Error
return false return false
end end
end
def write_reply(handle, err = 0, opts = {}) def write_reply(handle, err = 0, opts = {})
if opts[:magic] == :wrong if opts[:magic] == :wrong
@@ -77,16 +74,14 @@ module FlexNBD
@sock.write(::FlexNBD::REPLY_MAGIC) @sock.write(::FlexNBD::REPLY_MAGIC)
end end
@sock.write( [err].pack("N") ) @sock.write([err].pack('N'))
@sock.write(handle) @sock.write(handle)
end end
def close def close
@sock.close @sock.close
end end
def read_data(len) def read_data(len)
@sock.read(len) @sock.read(len)
end end
@@ -95,25 +90,27 @@ module FlexNBD
@sock.write(len) @sock.write(len)
end end
def getsockopt(level, optname)
@sock.getsockopt(level, optname)
end
def self.parse_be64(str) def self.parse_be64(str)
raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless
str.length == 8 str.length == 8
top, bottom = str.unpack("NN") top, bottom = str.unpack('NN')
(top << 32) + bottom (top << 32) + bottom
end end
def receive_mirror(opts = {}) def receive_mirror(opts = {})
write_hello() write_hello
loop do loop do
req = read_request req = read_request
case req[:type] case req[:type]
when 1 when 1
read_data(req[:len]) read_data(req[:len])
write_reply(req[:handle]) write_reply(req[:handle])
when 65536 when 65_536
write_reply(req[:handle], opts[:err] == :entrust ? 1 : 0) write_reply(req[:handle], opts[:err] == :entrust ? 1 : 0)
break break
else else
@@ -129,16 +126,13 @@ module FlexNBD
raise "Not a disconnect: #{req.inspect}" raise "Not a disconnect: #{req.inspect}"
end end
end end
end # class Client end # class Client
def initialize(addr, port) def initialize(addr, port)
@sock = TCPServer.new(addr, port) @sock = TCPServer.new(addr, port)
end end
def accept(err_msg = 'Timed out waiting for a connection', timeout = 5)
def accept( err_msg = "Timed out waiting for a connection", timeout = 5)
client_sock = nil client_sock = nil
begin begin
@@ -146,7 +140,7 @@ module FlexNBD
client_sock = @sock.accept client_sock = @sock.accept
end end
rescue Timeout::Error rescue Timeout::Error
raise Timeout::Error.new(err_msg) raise Timeout::Error, err_msg
end end
client_sock client_sock
@@ -154,13 +148,8 @@ module FlexNBD
Client.new(client_sock) Client.new(client_sock)
end end
def close def close
@sock.close @sock.close
end end
end # module FakeDest end # module FakeDest
end # module FlexNBD end # module FlexNBD

View File

@@ -1,12 +1,9 @@
# encoding: utf-8
require 'socket' require 'socket'
require "timeout" require 'timeout'
require 'flexnbd/constants' require 'flexnbd/constants'
module FlexNBD module FlexNBD
class FakeSource class FakeSource
def initialize(addr, port, err_msg, source_addr = nil, source_port = 0) def initialize(addr, port, err_msg, source_addr = nil, source_port = 0)
timing_out(2, err_msg) do timing_out(2, err_msg) do
begin begin
@@ -16,75 +13,76 @@ module FlexNBD
TCPSocket.new(addr, port) TCPSocket.new(addr, port)
end end
rescue Errno::ECONNREFUSED rescue Errno::ECONNREFUSED
$stderr.puts "Connection refused, retrying" warn 'Connection refused, retrying'
sleep(0.2) sleep(0.2)
retry retry
end end
end end
end end
def close def close
@sock.close @sock.close
end end
def read_hello
def read_hello()
timing_out(::FlexNBD::MS_HELLO_TIME_SECS, timing_out(::FlexNBD::MS_HELLO_TIME_SECS,
"Timed out waiting for hello." ) do 'Timed out waiting for hello.') do
fail "No hello." unless (hello = @sock.read( 152 )) && raise 'No hello.' unless (hello = @sock.read(152)) &&
hello.length == 152 hello.length == 152
magic_s = hello[0..7] passwd_s = hello[0..7]
ignore_s= hello[8..15] magic = hello[8..15].unpack('Q>').first
size_s = hello[16..23] size = hello[16..23].unpack('Q>').first
flags = hello[24..27].unpack('L>').first
reserved = hello[28..-1]
size_h, size_l = size_s.unpack("NN") return { passwd: passwd_s, magic: magic, size: size, flags: flags, reserved: reserved }
size = (size_h << 32) + size_l
return { :magic => magic_s, :size => size }
end end
end end
def send_request(type, handle = 'myhandle', from = 0, len = 0, magic = REQUEST_MAGIC, flags = 0)
def send_request( type, handle="myhandle", from=0, len=0, magic=REQUEST_MAGIC ) raise 'Bad handle' unless handle.length == 8
fail "Bad handle" unless handle.length == 8
@sock.write(magic) @sock.write(magic)
@sock.write( [type].pack( 'N' ) ) @sock.write([flags].pack('n'))
@sock.write([type].pack('n'))
@sock.write(handle) @sock.write(handle)
@sock.write([n64(from)].pack('q')) @sock.write([n64(from)].pack('q'))
@sock.write([len].pack('N')) @sock.write([len].pack('N'))
end end
def write_write_request(from, len, handle = 'myhandle')
def write_write_request( from, len, handle="myhandle" )
send_request(1, handle, from, len) send_request(1, handle, from, len)
end end
def write_write_request_with_fua(from, len, handle = 'myhandle')
def write_entrust_request( handle="myhandle" ) send_request(1, handle, from, len, REQUEST_MAGIC, 1)
send_request( 65536, handle )
end end
def write_disconnect_request( handle="myhandle" ) def write_flush_request(handle = 'myhandle')
send_request(3, handle, 0, 0)
end
def write_entrust_request(handle = 'myhandle')
send_request(65_536, handle)
end
def write_disconnect_request(handle = 'myhandle')
send_request(2, handle) send_request(2, handle)
end end
def write_read_request( from, len, handle="myhandle" ) def write_read_request(from, len, _handle = 'myhandle')
send_request( 0, "myhandle", from, len ) send_request(0, 'myhandle', from, len)
end end
def write_data(data) def write_data(data)
@sock.write(data) @sock.write(data)
end end
# Handy utility # Handy utility
def read(from, len) def read(from, len)
timing_out( 2, "Timed out reading" ) do timing_out(2, 'Timed out reading') do
send_request( 0, "myhandle", from, len ) send_request(0, 'myhandle', from, len)
read_raw(len) read_raw(len)
end end
end end
@@ -94,19 +92,26 @@ module FlexNBD
end end
def send_mirror def send_mirror
read_hello() read_hello
write( 0, "12345678" ) write(0, '12345678')
read_response() read_response
write_disconnect_request() write_disconnect_request
close() close
end end
def write(from, data) def write(from, data)
write_write_request(from, data.length) write_write_request(from, data.length)
write_data(data) write_data(data)
end end
def write_with_fua(from, data)
write_write_request_with_fua(from, data.length)
write_data(data)
end
def flush
write_flush_request
end
def read_response def read_response
magic = @sock.read(4) magic = @sock.read(4)
@@ -114,30 +119,26 @@ module FlexNBD
handle = @sock.read(8) handle = @sock.read(8)
{ {
:magic => magic, magic: magic,
:error => error_s.unpack("N").first, error: error_s.unpack('N').first,
:handle => handle handle: handle
} }
end end
def disconnected? def disconnected?
result = nil result = nil
Timeout.timeout( 2 ) { result = ( @sock.read(1) == nil ) } Timeout.timeout(2) { result = @sock.read(1).nil? }
result result
end end
def timing_out(time, msg) def timing_out(time, msg)
begin
Timeout.timeout(time) do Timeout.timeout(time) do
yield yield
end end
rescue Timeout::Error rescue Timeout::Error
$stderr.puts msg warn msg
exit 1 exit 1
end end
end
private private
@@ -154,7 +155,5 @@ module FlexNBD
((b & 0x000000000000ff00) << 40) | ((b & 0x000000000000ff00) << 40) |
((b & 0x00000000000000ff) << 56) ((b & 0x00000000000000ff) << 56)
end end
end # class FakeSource end # class FakeSource
end # module FlexNBD end # module FlexNBD

View File

@@ -0,0 +1,55 @@
require 'tempfile'
#
# LdPreload is a little wrapper for using LD_PRELOAD loggers to pick up system
# calls when testing flexnbd.
#
module LdPreload
#
# This takes an object name, sets up a temporary log file, whose name is
# recorded in the environment as OUTPUT_obj_name, where obj_name is the
# name of the preload module to build and load.
def with_ld_preload(obj_name)
@ld_preload_logs ||= {}
flunk 'Can only load a preload module once!' if @ld_preload_logs[obj_name]
system("make -C ld_preloads/ #{obj_name}.o > /dev/null") ||
flunk("Failed to build object #{obj_name}")
orig_env = ENV['LD_PRELOAD']
ENV['LD_PRELOAD'] = [orig_env, File.expand_path("./ld_preloads/#{obj_name}.o")].compact.join(' ')
# Open the log, and stick it in a hash
@ld_preload_logs[obj_name] = Tempfile.new(obj_name)
ENV['OUTPUT_' + obj_name] = @ld_preload_logs[obj_name].path
yield
ensure
if @ld_preload_logs[obj_name]
@ld_preload_logs[obj_name].close
@ld_preload_logs.delete(obj_name)
end
ENV['LD_PRELOAD'] = orig_env
end
def read_ld_preload_log(obj_name)
lines = []
lines << @ld_preload_logs[obj_name].readline.chomp until
@ld_preload_logs[obj_name].eof?
lines
end
#
# The next to methods assume the log file has one entry per line, and that
# each entry is a series of values separated by colons.
#
def parse_ld_preload_logs(obj_name)
read_ld_preload_log(obj_name).map do |l|
l.split(':').map { |i| i =~ /^\d+$/ ? i.to_i : i }
end
end
def assert_func_call(loglines, args, msg)
re = Regexp.new('^' + args.join(':'))
assert(loglines.any? { |l| l.match(re) }, msg)
end
end

View File

@@ -0,0 +1,13 @@
SRC := $(wildcard *.c)
OBJS := $(SRC:%.c=%.o)
all: $(OBJS)
clean:
$(RM) $(OBJS)
%.o: %.c
gcc -shared -fPIC -ldl -o $@ $<
.PHONY: all clean

View File

@@ -0,0 +1,33 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
typedef int (*real_msync_t)(void *addr, size_t length, int flags);
int real_msync(void *addr, size_t length, int flags) {
return ((real_msync_t)dlsym(RTLD_NEXT, "msync"))(addr, length, flags);
}
/*
* Noddy LD_PRELOAD wrapper to catch msync calls, and log them to a file.
*/
int msync(void *addr, size_t length, int flags) {
FILE *fd;
char *fn;
int retval;
retval = real_msync(addr, length, flags);
fn = getenv("OUTPUT_msync_logger");
if ( fn != NULL ) {
fd = fopen(fn,"a");
fprintf(fd,"msync:%d:%i:%i:%i\n", addr, length, flags, retval);
fclose(fd);
}
return retval;
}

View File

@@ -0,0 +1,38 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
/*
* Noddy LD_PRELOAD wrapper to catch setsockopt calls, and log them to a file.
*/
typedef int (*real_setsockopt_t)(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int real_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
return ((real_setsockopt_t)dlsym(RTLD_NEXT, "setsockopt"))(sockfd, level, optname, optval, optlen);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
FILE *fd;
char *fn;
int retval;
retval = real_setsockopt(sockfd, level, optname, optval, optlen);
fn = getenv("OUTPUT_setsockopt_logger");
/*
* Only interested in catching non-null 4-byte (integer) values
*/
if ( fn != NULL && optval != NULL && optlen == 4) {
fd = fopen(fn,"a");
fprintf(fd,"setsockopt:%i:%i:%i:%i:%i\n", sockfd, level, optname, *(int *)optval, retval);
fclose(fd);
}
return retval;
}

2
tests/acceptance/nbd_scenarios Normal file → Executable file
View File

@@ -1,6 +1,6 @@
#!/usr/bin/ruby #!/usr/bin/ruby
test_files = Dir[File.dirname( __FILE__ ) + "/test*.rb"] test_files = Dir[File.dirname(__FILE__) + '/test*.rb']
for filename in test_files for filename in test_files
require filename require filename
end end

View File

@@ -1,8 +1,12 @@
# encoding: utf-8
require 'flexnbd/fake_source' require 'flexnbd/fake_source'
require 'flexnbd/fake_dest' require 'flexnbd/fake_dest'
require 'ld_preload'
module ProxyTests module ProxyTests
include LdPreload
def b def b
"\xFF".b "\xFF".b
end end
@@ -13,14 +17,17 @@ module ProxyTests
@env.nbd2.can_die(0) @env.nbd2.can_die(0)
client = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy") client = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy")
begin begin
result = client.read_hello result = client.read_hello
assert_equal "NBDMAGIC", result[:magic] assert_equal 'NBDMAGIC', result[:passwd]
assert_equal override_size || @env.file1.size, result[:size] assert_equal override_size || @env.file1.size, result[:size]
yield client yield client
ensure ensure
client.close rescue nil begin
client.close
rescue StandardError
nil
end
end end
end end
@@ -32,11 +39,11 @@ module ProxyTests
with_proxied_client do |client| with_proxied_client do |client|
(0..3).each do |n| (0..3).each do |n|
offset = n * 4096 offset = n * 4096
client.write_read_request(offset, 4096, "myhandle") client.write_read_request(offset, 4096, 'myhandle')
rsp = client.read_response rsp = client.read_response
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal "myhandle", rsp[:handle] assert_equal 'myhandle', rsp[:handle]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
orig_data = @env.file1.read(offset, 4096) orig_data = @env.file1.read(offset, 4096)
@@ -59,7 +66,7 @@ module ProxyTests
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal "myhandle", rsp[:handle] assert_equal 'myhandle', rsp[:handle]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
data = @env.file1.read(offset, 4096) data = @env.file1.read(offset, 4096)
@@ -69,6 +76,20 @@ module ProxyTests
end end
end end
def test_write_request_past_end_of_disc_returns_to_client
with_proxied_client do |client|
n = 1000
offset = n * 4096
client.write(offset, b * 4096)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 'myhandle', rsp[:handle]
# NBD protocol say ENOSPC (28) in this situation
assert_equal 28, rsp[:error]
end
end
def make_fake_server def make_fake_server
server = FlexNBD::FakeDest.new(@env.ip, @env.port1) server = FlexNBD::FakeDest.new(@env.ip, @env.port1)
@server_up = true @server_up = true
@@ -114,37 +135,41 @@ module ProxyTests
sc2.write_data(b * 4096) sc2.write_data(b * 4096)
# Check it to make sure it's correct # Check it to make sure it's correct
rsp = timeout(15) { client.read_response } rsp = Timeout.timeout(15) { client.read_response }
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle] assert_equal req1[:handle], rsp[:handle]
data = client.read_raw(4096) data = client.read_raw(4096)
assert_equal( (b * 4096), data, "Wrong data returned" ) assert_equal((b * 4096), data, 'Wrong data returned')
sc2.close sc2.close
server.close server.close
end end
end end
def test_write_request_retried_when_upstream_dies_partway def test_write_request_retried_when_upstream_dies_partway
maker = make_fake_server maker = make_fake_server
with_ld_preload('setsockopt_logger') do
with_proxied_client(4096) do |client| with_proxied_client(4096) do |client|
server, sc1 = maker.value server, sc1 = maker.value
# Send the read request to the proxy # Send the read request to the proxy
client.write(0, (b * 4096)) client.write(0, (b * 4096))
# ensure we're given the read request # ensure we're given the write request
req1 = sc1.read_request req1 = sc1.read_request
assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic] assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic]
assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type] assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type]
assert_equal 0, req1[:from] assert_equal 0, req1[:from]
assert_equal 4096, req1[:len] assert_equal 4096, req1[:len]
data1 = sc1.read_data(4096) data1 = sc1.read_data(4096)
assert_equal( ( b * 4096 ), data1, "Data not proxied successfully" ) assert_equal((b * 4096), data1, 'Data not proxied successfully')
# Read the setsockopt logs, so we can check that TCP_NODELAY is re-set
# later
read_ld_preload_log('setsockopt_logger')
# Kill the server again, now we're sure the read request has been sent once # Kill the server again, now we're sure the read request has been sent once
sc1.close sc1.close
@@ -163,32 +188,109 @@ module ProxyTests
sc2.write_reply(req2[:handle]) sc2.write_reply(req2[:handle])
# Check it to make sure it's correct # Check it to make sure it's correct
rsp = timeout(15) { client.read_response } rsp = Timeout.timeout(15) { client.read_response }
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle] assert_equal req1[:handle], rsp[:handle]
sc2.close sc2.close
server.close server.close
# Check TCP_NODELAY was set on the upstream socket
log = read_ld_preload_log('setsockopt_logger')
assert_func_call(log,
['setsockopt', 3,
Socket::SOL_TCP, Socket::TCP_NODELAY, 1, 0],
'TCP_NODELAY not set on upstream fd 3')
end
end
end
def test_write_request_retried_when_upstream_times_out_during_write_phase
ENV['FLEXNBD_UPSTREAM_TIMEOUT'] = '4'
maker = make_fake_server
with_ld_preload('setsockopt_logger') do
with_proxied_client(4096) do |client|
server, sc1 = maker.value
# Guess an approprate request size, based on the send buffer size.
sz = sc1.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).int * 4
data1 = (b * sz)
# Send the read request to the proxy
client.write(0, data1)
# ensure we're given the write request
req1 = sc1.read_request
assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic]
assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type]
assert_equal 0, req1[:from]
assert_equal data1.size, req1[:len]
# We do not read it at this point, as we want the proxy to be waiting
# in the WRITE_UPSTREAM state.
# Need to sleep longer than the timeout set above
sleep 5
# Check the number of bytes that can be read from the socket without
# blocking. If this equal to the size of the original request, then
# the whole request has been buffered. If this is the case, then the
# proxy will not time-out in the WRITE_UPSTREAM statem which is what
# we're trying to test.
assert sc1.nread < sz, 'Request from proxy completely buffered. Test is useless'
# Kill the server now that the timeout has happened.
sc1.close
# We expect the proxy to reconnect without our client doing anything.
sc2 = server.accept
sc2.write_hello
# And once reconnected, it should resend an identical request.
req2 = sc2.read_request
assert_equal req1, req2
# And now lets read the data to make sure we get it all.
data2 = sc2.read_data(req2[:len])
assert_equal data1, data2
sc2.close
server.close
end
end end
end end
def test_only_one_client_can_connect_to_proxy_at_a_time def test_only_one_client_can_connect_to_proxy_at_a_time
with_proxied_client do |client| with_proxied_client do |_client|
c2 = nil c2 = nil
assert_raises(Timeout::Error) do assert_raises(Timeout::Error) do
timeout(1) do Timeout.timeout(1) do
c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)") c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)")
c2.read_hello c2.read_hello
end end
end end
c2.close rescue nil if c2 if c2
begin
c2.close
rescue StandardError
nil
end
end
end
end end
def test_maximum_write_request_size
# Defined in src/common/nbdtypes.h
nbd_max_block_size = 32 * 1024 * 1024
@env.writefile1('0' * 40 * 1024)
with_proxied_client do |client|
# This will crash with EPIPE if the proxy dies.
client.write(0, b * nbd_max_block_size)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
end
end end
end end

View File

@@ -1,13 +1,10 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
class TestDestErrorHandling < Test::Unit::TestCase class TestDestErrorHandling < Test::Unit::TestCase
def setup def setup
@env = Environment.new @env = Environment.new
@env.writefile1( "0" * 4 ) @env.writefile1('0' * 4)
@env.listen1 @env.listen1
end end
@@ -15,89 +12,77 @@ class TestDestErrorHandling < Test::Unit::TestCase
@env.cleanup @env.cleanup
end end
def test_hello_blocked_by_disconnect_causes_error_not_fatal def test_hello_blocked_by_disconnect_causes_error_not_fatal
run_fake( "source/close_after_connect" ) run_fake('source/close_after_connect')
assert_no_control assert_no_control
end end
=begin # # This is disabled while CLIENT_MAX_WAIT_SECS is removed
# This is disabled while CLIENT_MAX_WAIT_SECS is removed # def test_hello_goes_astray_causes_timeout_error
def test_hello_goes_astray_causes_timeout_error # run_fake( "source/hang_after_hello" )
run_fake( "source/hang_after_hello" ) # assert_no_control
assert_no_control # end
end
=end
def test_sigterm_has_bad_exit_status def test_sigterm_has_bad_exit_status
@env.nbd1.can_die(1) @env.nbd1.can_die(1)
run_fake( "source/sigterm_after_hello" ) run_fake('source/sigterm_after_hello')
end end
def test_disconnect_after_hello_causes_error_not_fatal def test_disconnect_after_hello_causes_error_not_fatal
run_fake( "source/close_after_hello" ) run_fake('source/close_after_hello')
assert_no_control assert_no_control
end end
def test_partial_read_causes_error def test_partial_read_causes_error
run_fake( "source/close_mid_read" ) run_fake('source/close_mid_read')
end end
def test_double_connect_during_hello def test_double_connect_during_hello
run_fake( "source/connect_during_hello" ) run_fake('source/connect_during_hello')
end end
def test_acl_rejection def test_acl_rejection
@env.acl1("127.0.0.1") @env.acl1('127.0.0.1')
run_fake( "source/connect_from_banned_ip") run_fake('source/connect_from_banned_ip')
end end
def test_bad_write def test_bad_write
run_fake( "source/write_out_of_range" ) run_fake('source/write_out_of_range')
end end
def test_disconnect_before_write_data_causes_error def test_disconnect_before_write_data_causes_error
run_fake( "source/close_after_write" ) run_fake('source/close_after_write')
end end
def test_disconnect_before_write_reply_causes_error def test_disconnect_before_write_reply_causes_error
# Note that this is an odd case: writing the reply doesn't fail. # Note that this is an odd case: writing the reply doesn't fail.
# The test passes because the next attempt by flexnbd to read a # The test passes because the next attempt by flexnbd to read a
# request returns EOF. # request returns EOF.
run_fake( "source/close_after_write_data" ) run_fake('source/close_after_write_data')
end end
def test_straight_migration def test_straight_migration
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
run_fake( "source/successful_transfer" ) run_fake('source/successful_transfer')
end end
private private
def run_fake(name) def run_fake(name)
@env.run_fake(name, @env.ip, @env.port1) @env.run_fake(name, @env.ip, @env.port1)
assert @env.fake_reports_success, "#{name} failed." assert @env.fake_reports_success, "#{name} failed."
end end
def status def status
stat, _ = @env.status1 stat, = @env.status1
stat stat
end end
def assert_no_control def assert_no_control
assert !status['has_control'], "Thought it had control" assert !status['has_control'], 'Thought it had control'
end end
def assert_control def assert_control
assert status['has_control'], "Didn't think it had control" assert status['has_control'], "Didn't think it had control"
end end
end # class TestDestErrorHandling end # class TestDestErrorHandling

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
require 'flexnbd/constants' require 'flexnbd/constants'
@@ -19,20 +17,18 @@ class TestHappyPath < Test::Unit::TestCase
@env.cleanup @env.cleanup
end end
def test_read1 def test_read1
@env.writefile1("f"*64) @env.writefile1('f' * 64)
@env.serve1 @env.serve1
[0, 12, 63].each do |num| [0, 12, 63].each do |num|
assert_equal( assert_equal(
bin(@env.nbd1.read(num * @env.blocksize, @env.blocksize)), bin(@env.nbd1.read(num * @env.blocksize, @env.blocksize)),
bin(@env.file1.read(num * @env.blocksize, @env.blocksize)) bin(@env.file1.read(num * @env.blocksize, @env.blocksize))
) )
end end
[124, 1200, 10028, 25488].each do |num| [124, 1200, 10_028, 25_488].each do |num|
assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4))) assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4)))
end end
end end
@@ -40,11 +36,11 @@ class TestHappyPath < Test::Unit::TestCase
# Check that we're not # Check that we're not
# #
def test_writeread1 def test_writeread1
@env.writefile1("0"*64) @env.writefile1('0' * 64)
@env.serve1 @env.serve1
[0, 12, 63].each do |num| [0, 12, 63].each do |num|
data = "X"*@env.blocksize data = 'X' * @env.blocksize
@env.nbd1.write(num * @env.blocksize, data) @env.nbd1.write(num * @env.blocksize, data)
assert_equal(data, @env.file1.read(num * @env.blocksize, data.size)) assert_equal(data, @env.file1.read(num * @env.blocksize, data.size))
assert_equal(data, @env.nbd1.read(num * @env.blocksize, data.size)) assert_equal(data, @env.nbd1.read(num * @env.blocksize, data.size))
@@ -55,11 +51,11 @@ class TestHappyPath < Test::Unit::TestCase
# up. # up.
# #
def test_writeread2 def test_writeread2
@env.writefile1("0"*1024) @env.writefile1('0' * 1024)
@env.serve1 @env.serve1
d0 = "\0" * @env.blocksize d0 = "\0" * @env.blocksize
d1 = "X"*@env.blocksize d1 = 'X' * @env.blocksize
(0..63).each do |num| (0..63).each do |num|
@env.nbd1.write(num * @env.blocksize * 2, d1) @env.nbd1.write(num * @env.blocksize * 2, d1)
end end
@@ -68,20 +64,18 @@ class TestHappyPath < Test::Unit::TestCase
end end
end end
def setup_to_mirror def setup_to_mirror
@env.writefile1( "f"*4 ) @env.writefile1('f' * 4)
@env.serve1 @env.serve1
@env.writefile2( "0"*4 ) @env.writefile2('0' * 4)
@env.listen2 @env.listen2
end end
def test_mirror def test_mirror
@env.nbd1.can_die @env.nbd1.can_die
@env.nbd2.can_die(0) @env.nbd2.can_die(0)
setup_to_mirror() setup_to_mirror
stdout, stderr = @env.mirror12 stdout, stderr = @env.mirror12
@@ -89,16 +83,15 @@ class TestHappyPath < Test::Unit::TestCase
@env.nbd2.join @env.nbd2.join
assert(File.file?(@env.filename1), assert(File.file?(@env.filename1),
"The source file was incorrectly deleted") 'The source file was incorrectly deleted')
assert_equal(@env.file1.read_original(0, @env.blocksize), assert_equal(@env.file1.read_original(0, @env.blocksize),
@env.file2.read(0, @env.blocksize)) @env.file2.read(0, @env.blocksize))
end end
def test_mirror_unlink def test_mirror_unlink
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
@env.nbd2.can_die(0) @env.nbd2.can_die(0)
setup_to_mirror() setup_to_mirror
assert File.file?(@env.filename1) assert File.file?(@env.filename1)
@@ -106,45 +99,40 @@ class TestHappyPath < Test::Unit::TestCase
assert_no_match(/unrecognized/, stderr) assert_no_match(/unrecognized/, stderr)
Timeout.timeout(10) { @env.nbd1.join }
Timeout.timeout(10) do @env.nbd1.join end
assert !File.file?(@env.filename1) assert !File.file?(@env.filename1)
end end
def test_write_to_high_block def test_write_to_high_block
# #
# This test does not work on 32 bit platforms. # This test does not work on 32 bit platforms.
# #
skip("Not relevant on 32-bit platforms") if ( ["a"].pack("p").size < 8 ) skip('Not relevant on 32-bit platforms') if ['a'].pack('p').size < 8
# Create a large file, then try to write to somewhere after the 2G boundary # Create a large file, then try to write to somewhere after the 2G boundary
@env.truncate1 "4G" @env.truncate1 '4G'
@env.serve1 @env.serve1
@env.nbd1.write( 2**31+2**29, "12345678" ) @env.nbd1.write(2**31 + 2**29, '12345678')
sleep(1) sleep(1)
assert_equal "12345678", @env.nbd1.read( 2**31+2**29, 8 ) assert_equal '12345678', @env.nbd1.read(2**31 + 2**29, 8)
end end
def test_set_acl def test_set_acl
# Just check that we get sane feedback here # Just check that we get sane feedback here
@env.writefile1( "f"*4 ) @env.writefile1('f' * 4)
@env.serve1 @env.serve1
_,stderr = @env.acl1("127.0.0.1") _, stderr = @env.acl1('127.0.0.1')
assert_no_match(/^(F|E):/, stderr) assert_no_match(/^(F|E):/, stderr)
end end
def test_write_more_than_one_run def test_write_more_than_one_run
one_mb = 2**20 one_mb = 2**20
data = "\0" * 256 * one_mb data = "\0" * 256 * one_mb
File.open(@env.filename1, "wb") do |f| f.write( "1" * 256 * one_mb ) end File.open(@env.filename1, 'wb') { |f| f.write('1' * 256 * one_mb) }
@env.serve1 @env.serve1
sleep 5 sleep 5
@@ -153,17 +141,15 @@ class TestHappyPath < Test::Unit::TestCase
@env.nbd1.kill @env.nbd1.kill
i = 0 i = 0
File.open(@env.filename1, "rb") do |f| File.open(@env.filename1, 'rb') do |f|
while mb = f.read(one_mb) while mb = f.read(one_mb)
unless "\0" * one_mb == mb unless "\0" * one_mb == mb
msg = "Read non-zeros after offset %x:\n"%(i * one_mb) msg = format("Read non-zeros after offset %x:\n", (i * one_mb))
msg += `hexdump #{@env.filename1} | head -n5` msg += `hexdump #{@env.filename1} | head -n5`
fail msg raise msg
end end
i += 1 i += 1
end end
end end
end end
end end

View File

@@ -2,7 +2,6 @@ require 'test/unit'
require 'environment' require 'environment'
require 'proxy_tests' require 'proxy_tests'
class TestPrefetchProxyMode < Test::Unit::TestCase class TestPrefetchProxyMode < Test::Unit::TestCase
include ProxyTests include ProxyTests
@@ -10,7 +9,7 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
super super
@env = Environment.new @env = Environment.new
@env.prefetch_proxy! @env.prefetch_proxy!
@env.writefile1( "f" * 16 ) @env.writefile1('f' * 16)
end end
def teardown def teardown
@@ -18,5 +17,3 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
super super
end end
end end

View File

@@ -2,14 +2,13 @@ require 'test/unit'
require 'environment' require 'environment'
require 'proxy_tests' require 'proxy_tests'
class TestProxyMode < Test::Unit::TestCase class TestProxyMode < Test::Unit::TestCase
include ProxyTests include ProxyTests
def setup def setup
super super
@env = Environment.new @env = Environment.new
@env.writefile1( "f" * 16 ) @env.writefile1('f' * 16)
end end
def teardown def teardown
@@ -17,4 +16,3 @@ class TestProxyMode < Test::Unit::TestCase
super super
end end
end end

View File

@@ -1,15 +1,15 @@
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
require 'flexnbd/fake_source' require 'flexnbd/fake_source'
require 'ld_preload'
class TestServeMode < Test::Unit::TestCase class TestServeMode < Test::Unit::TestCase
include LdPreload
def setup def setup
super super
@b = "\xFF".b @b = "\xFF".b
@env = Environment.new @env = Environment.new
@env.writefile1( "0" )
@env.serve1
end end
def teardown def teardown
@@ -18,35 +18,44 @@ class TestServeMode < Test::Unit::TestCase
end end
def connect_to_server def connect_to_server
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, "Connecting to server failed") @env.writefile1('0')
@env.serve1
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
begin begin
result = client.read_hello result = client.read_hello
assert_equal "NBDMAGIC", result[:magic] assert_equal 'NBDMAGIC', result[:passwd]
assert_equal 0x00420281861253, result[:magic]
assert_equal @env.file1.size, result[:size] assert_equal @env.file1.size, result[:size]
# See src/common/nbdtypes.h for the various flags. At the moment we
# support HAS_FLAGS (1), SEND_FLUSH (4), SEND_FUA (8)
assert_equal (1 | 4 | 8), result[:flags]
assert_equal "\x0" * 124, result[:reserved]
yield client yield client
ensure ensure
client.close rescue nil begin
client.close
rescue StandardError
nil
end
end end
end end
def test_bad_request_magic_receives_error_response def test_bad_request_magic_receives_error_response
connect_to_server do |client| connect_to_server do |client|
# replace REQUEST_MAGIC with all 0s to make it look bad # replace REQUEST_MAGIC with all 0s to make it look bad
client.send_request( 0, "myhandle", 0, 0, "\x00\x00\x00\x00" ) client.send_request(0, 'myhandle', 0, 0, "\x00\x00\x00\x00")
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal "myhandle", rsp[:handle] assert_equal 'myhandle', rsp[:handle]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
# The client should be disconnected now # The client should be disconnected now
assert client.disconnected?, "Server not disconnected" assert client.disconnected?, 'Server not disconnected'
end end
end end
def test_long_write_on_top_of_short_write_is_respected def test_long_write_on_top_of_short_write_is_respected
connect_to_server do |client| connect_to_server do |client|
# Start with a file of all-zeroes. # Start with a file of all-zeroes.
client.write(0, "\x00" * @env.file1.size) client.write(0, "\x00" * @env.file1.size)
@@ -68,15 +77,15 @@ class TestServeMode < Test::Unit::TestCase
assert_equal @b * 2, @env.file1.read(0, 2) assert_equal @b * 2, @env.file1.read(0, 2)
end end
def test_read_request_out_of_bounds_receives_error_response def test_read_request_out_of_bounds_receives_error_response
connect_to_server do |client| connect_to_server do |client|
client.write_read_request(@env.file1.size, 4096) client.write_read_request(@env.file1.size, 4096)
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal "myhandle", rsp[:handle] assert_equal 'myhandle', rsp[:handle]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" # NBD protocol suggests ENOSPC (28) is returned
assert_equal 28, rsp[:error], 'Server sent incorrect response'
# Ensure we're not disconnected by sending a request. We don't care about # Ensure we're not disconnected by sending a request. We don't care about
# whether the reply is good or not, here. # whether the reply is good or not, here.
@@ -92,8 +101,9 @@ class TestServeMode < Test::Unit::TestCase
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal "myhandle", rsp[:handle] assert_equal 'myhandle', rsp[:handle]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" # NBD protocol suggests ENOSPC (28) is returned
assert_equal 28, rsp[:error], 'Server sent incorrect response'
# Ensure we're not disconnected by sending a request. We don't care about # Ensure we're not disconnected by sending a request. We don't care about
# whether the reply is good or not, here. # whether the reply is good or not, here.
@@ -101,10 +111,113 @@ class TestServeMode < Test::Unit::TestCase
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
end end
end end
def test_unknown_command_receives_error_response
connect_to_server do |client|
client.send_request(123)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 'myhandle', rsp[:handle]
# NBD protocol suggests EINVAL (22) is returned
assert_equal 22, rsp[:error], 'Server sent incorrect response'
# Ensure we're not disconnected by sending a request. We don't care about
# whether the reply is good or not, here.
client.write(0, "\x00" * @env.file1.size)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
end
end end
def test_flush_is_accepted
with_ld_preload('msync_logger') do
connect_to_server do |client|
client.flush
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
end
op = parse_ld_preload_logs('msync_logger')
assert_equal 1, op.count, 'Only one msync expected'
assert_equal @env.blocksize, op.first[2], 'msync length wrong'
assert_equal 6, op.first[3], 'msync called with incorrect flags'
end
end
def test_write_with_fua_is_accepted
with_ld_preload('msync_logger') do
page_size = Integer(`getconf PAGESIZE`)
@env.blocksize = page_size * 10
connect_to_server do |client|
# Write somewhere in the third page
pos = page_size * 3 + 100
client.write_with_fua(pos, "\x00" * 33)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
end
op = parse_ld_preload_logs('msync_logger')
assert_equal 1, op.count, 'Only one msync expected'
# Should be 100 + 33, as we've started writing 100 bytes into a page, for
# 33 bytes
assert_equal 133, op.first[2], 'msync length wrong'
assert_equal 6, op.first[3], 'msync called with incorrect flags'
end
end
def test_odd_size_discs_are_truncated_to_nearest_512
# This should get rounded down to 1024
@env.blocksize = 1024 + 511
@env.writefile1('0')
@env.serve1
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
begin
result = client.read_hello
assert_equal 'NBDMAGIC', result[:passwd]
assert_equal 0x00420281861253, result[:magic]
assert_equal 1024, result[:size]
client.close
end
end
def test_server_sets_tcpkeepalive
with_ld_preload('setsockopt_logger') do
connect_to_server(&:close)
op = read_ld_preload_log('setsockopt_logger')
assert_func_call(op,
['setsockopt', '\d+',
Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1, 0],
'TCP Keepalive not successfully set')
assert_func_call(op,
['setsockopt', '\d+',
Socket::SOL_TCP, Socket::TCP_KEEPIDLE, 30, 0],
'TCP Keepalive idle timeout not set to 30s')
assert_func_call(op,
['setsockopt', '\d+',
Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10, 0],
'TCP keepalive retry time not set to 10s')
assert_func_call(op,
['setsockopt', '\d+',
Socket::SOL_TCP, Socket::TCP_KEEPCNT, 3, 0],
'TCP keepalive count not set to 3')
end
end
def test_status_returns_correct_client_count
@env.writefile1('0')
@env.serve1
assert_equal('0', @env.status1['num_clients'])
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
assert_equal('1', @env.status1['num_clients'])
client2 = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
assert_equal('2', @env.status1['num_clients'])
client2.close
client.close
assert_equal('0', @env.status1['num_clients'])
end
end

View File

@@ -1,126 +1,105 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
class TestSourceErrorHandling < Test::Unit::TestCase class TestSourceErrorHandling < Test::Unit::TestCase
def setup def setup
@old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] @old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS']
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = "4.0" ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = '4.0'
@env = Environment.new @env = Environment.new
@env.writefile1( "f" * 4 ) @env.writefile1('f' * 4)
@env.serve1 @env.serve1
end end
def teardown def teardown
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
@env.cleanup @env.cleanup
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env
end end
def expect_term_during_migration def expect_term_during_migration
@env.nbd1.can_die(1, 9) @env.nbd1.can_die(1, 9)
end end
def test_failure_to_connect_reported_in_mirror_cmd_response def test_failure_to_connect_reported_in_mirror_cmd_response
stdout, stderr = @env.mirror12_unchecked stdout, stderr = @env.mirror12_unchecked
expect_term_during_migration expect_term_during_migration
assert_match(/failed to connect/, stderr) assert_match(/failed to connect/, stderr)
end end
def test_sigterm_after_hello_quits_with_status_of_1 def test_sigterm_after_hello_quits_with_status_of_1
expect_term_during_migration expect_term_during_migration
run_fake( "dest/sigterm_after_hello" ) run_fake('dest/sigterm_after_hello')
end end
def test_destination_hangs_after_connect_reports_error_at_source def test_destination_hangs_after_connect_reports_error_at_source
run_fake( "dest/hang_after_connect", run_fake('dest/hang_after_connect',
:err => /Remote server failed to respond/ ) err: /Remote server failed to respond/)
end end
def test_destination_rejects_connection_reports_error_at_source def test_destination_rejects_connection_reports_error_at_source
run_fake( "dest/reject_acl", run_fake('dest/reject_acl',
:err => /Mirror was rejected/ ) err: /Mirror was rejected/)
end end
def test_wrong_size_causes_disconnect def test_wrong_size_causes_disconnect
run_fake( "dest/hello_wrong_size", run_fake('dest/hello_wrong_size',
:err => /Remote size does not match local size/ ) err: /Remote size does not match local size/)
end end
def test_wrong_magic_causes_disconnect def test_wrong_magic_causes_disconnect
expect_term_during_migration expect_term_during_migration
run_fake( "dest/hello_wrong_magic", run_fake('dest/hello_wrong_magic',
:err => /Mirror was rejected/ ) err: /Mirror was rejected/)
end end
def test_disconnect_after_hello_causes_retry def test_disconnect_after_hello_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/close_after_hello", run_fake('dest/close_after_hello',
:out => /Mirror started/ ) out: /Mirror started/)
end end
def test_write_times_out_causes_retry def test_write_times_out_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/hang_after_write" ) run_fake('dest/hang_after_write')
end end
def test_rejected_write_causes_retry def test_rejected_write_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/error_on_write" ) run_fake('dest/error_on_write')
end end
def test_disconnect_before_write_reply_causes_retry def test_disconnect_before_write_reply_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/close_after_write" ) run_fake('dest/close_after_write')
end end
def test_bad_write_reply_causes_retry def test_bad_write_reply_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/write_wrong_magic" ) run_fake('dest/write_wrong_magic')
end end
def test_pre_entrust_disconnect_causes_retry def test_pre_entrust_disconnect_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake( "dest/close_after_writes" ) run_fake('dest/close_after_writes')
end end
def test_cancel_migration def test_cancel_migration
run_fake( "dest/break_after_hello" ) run_fake('dest/break_after_hello')
end end
private private
def run_fake(name, opts = {}) def run_fake(name, opts = {})
@env.run_fake(name, @env.ip, @env.port2, @env.nbd1.ctrl) @env.run_fake(name, @env.ip, @env.port2, @env.nbd1.ctrl)
stdout, stderr = @env.mirror12_unchecked stdout, stderr = @env.mirror12_unchecked
assert_success assert_success
assert_match(opts[:err], stderr) if opts[:err] assert_match(opts[:err], stderr) if opts[:err]
assert_match(opts[:out], stdout) if opts[:out] assert_match(opts[:out], stdout) if opts[:out]
return stdout, stderr [stdout, stderr]
end end
def assert_success(msg = nil) def assert_success(msg = nil)
assert @env.fake_reports_success, msg || "Fake failed" assert @env.fake_reports_success, msg || 'Fake failed'
end end
end # class TestSourceErrorHandling end # class TestSourceErrorHandling

View File

@@ -9,114 +9,132 @@ require 'tmpdir'
Thread.abort_on_exception = true Thread.abort_on_exception = true
class TestWriteDuringMigration < Test::Unit::TestCase class TestWriteDuringMigration < Test::Unit::TestCase
def setup def setup
@flexnbd = File.expand_path("../../build/flexnbd") @flexnbd = File.expand_path('../../build/flexnbd')
raise "No binary!" unless File.executable?( @flexnbd )
raise 'No binary!' unless File.executable?(@flexnbd)
@size = 20 * 1024 * 1024 # 20MB @size = 20 * 1024 * 1024 # 20MB
@write_data = "foo!" * 2048 # 8K write @write_data = 'foo!' * 2048 # 8K write
@source_port = 9990 @source_port = 9990
@dest_port = 9991 @dest_port = 9991
@source_sock = "src.sock" @source_sock = 'src.sock'
@dest_sock = "dst.sock" @dest_sock = 'dst.sock'
@source_file = "src.file" @source_file = 'src.file'
@dest_file = "dst.file" @dest_file = 'dst.file'
end end
def teardown def teardown
[@dst_proc, @src_proc].each do |pid| [@dst_proc, @src_proc].each do |pid|
if pid next unless pid
Process.kill( "KILL", pid ) rescue nil begin
Process.kill('KILL', pid)
rescue StandardError
nil
end end
end end
end end
def debug_arg def debug_arg
ENV['DEBUG'] ? "--verbose" : "" ENV['DEBUG'] ? '--verbose' : ''
end end
def launch_servers def launch_servers
@dst_proc = fork() { @dst_proc = fork do
cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}" cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}"
exec cmd exec cmd
} end
@src_proc = fork() { @src_proc = fork do
cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}" cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}"
exec cmd exec cmd
} end
begin begin
awaiting = nil awaiting = nil
Timeout.timeout(10) do Timeout.timeout(10) do
awaiting = :source awaiting = :source
sleep 0.1 while !File.exists?( @source_sock ) sleep 0.1 until File.exist?(@source_sock)
awaiting = :dest awaiting = :dest
sleep 0.1 while !File.exists?( @dest_sock ) sleep 0.1 until File.exist?(@dest_sock)
end end
rescue Timeout::Error rescue Timeout::Error
case awaiting case awaiting
when :source when :source
fail "Couldn't get a source socket." raise "Couldn't get a source socket."
when :dest when :dest
fail "Couldn't get a destination socket." raise "Couldn't get a destination socket."
else else
fail "Something went wrong I don't understand." raise "Something went wrong I don't understand."
end end
end end
end end
def make_files
def make_files()
FileUtils.touch(@source_file) FileUtils.touch(@source_file)
File.truncate(@source_file, @size) File.truncate(@source_file, @size)
FileUtils.touch(@dest_file) FileUtils.touch(@dest_file)
File.truncate(@dest_file, @size) File.truncate(@dest_file, @size)
File.open(@source_file, "wb"){|f| f.write "a"*@size } File.open(@source_file, 'wb') { |f| f.write 'a' * @size }
end end
def start_mirror def start_mirror
UNIXSocket.open(@source_sock) {|sock| UNIXSocket.open(@source_sock) do |sock|
sock.write(["mirror", "127.0.0.1", @dest_port.to_s, "exit"].join("\x0A") + "\x0A\x0A") sock.write(['mirror', '127.0.0.1', @dest_port.to_s, 'exit'].join("\x0A") + "\x0A\x0A")
sock.flush sock.flush
rsp = sock.readline sock.readline
} end
end end
def stop_mirror
UNIXSocket.open(@source_sock) do |sock|
sock.write("break\x0A\x0A")
sock.flush
sock.readline
end
end
def wait_for_quit() def wait_for_quit
Timeout.timeout(10) do Timeout.timeout(10) do
start_time = Time.now Process.waitpid2(@dst_proc)
dst_result = Process::waitpid2(@dst_proc) Process.waitpid2(@src_proc)
src_result = Process::waitpid2(@src_proc)
end end
end end
def source_writer def source_writer
client = FlexNBD::FakeSource.new( "127.0.0.1", @source_port, "Timed out connecting" ) client = FlexNBD::FakeSource.new('127.0.0.1', @source_port, 'Timed out connecting')
offsets = Range.new(0, (@size - @write_data.size) / 4096).to_a offsets = Range.new(0, (@size - @write_data.size) / 4096).to_a
loop do loop do
begin begin
client.write(offsets[rand(offsets.size)] * 4096, @write_data) client.write(offsets[rand(offsets.size)] * 4096, @write_data)
rescue => err rescue StandardError
# We expect a broken write at some point, so ignore it # We expect a broken write at some point, so ignore it
break break
end end
end end
end end
def bombard_with_status
loop do
begin
UNIXSocket.open(@source_sock) do |sock|
sock.write("status\x0A\x0A")
sock.flush
sock.readline
end
rescue StandardError
# If the socket disappears, that's OK.
break
end
end
end
def assert_both_sides_identical def assert_both_sides_identical
# puts `md5sum #{@source_file} #{@dest_file}` # puts `md5sum #{@source_file} #{@dest_file}`
# Ensure each block matches # Ensure each block matches
File.open(@source_file, "r") do |source| File.open(@source_file, 'r') do |source|
File.open(@dest_file, "r") do |dest| File.open(@dest_file, 'r') do |dest|
0.upto(@size / 4096) do |block_num| 0.upto(@size / 4096) do |block_num|
s_data = source.read(4096) s_data = source.read(4096)
d_data = dest.read(4096) d_data = dest.read(4096)
@@ -131,16 +149,16 @@ class TestWriteDuringMigration < Test::Unit::TestCase
end end
def test_write_during_migration def test_write_during_migration
Dir.mktmpdir() do |tmpdir| Dir.mktmpdir do |tmpdir|
Dir.chdir(tmpdir) do Dir.chdir(tmpdir) do
make_files() make_files
launch_servers() launch_servers
src_writer = Thread.new { source_writer } src_writer = Thread.new { source_writer }
start_mirror() start_mirror
wait_for_quit() wait_for_quit
src_writer.join src_writer.join
assert_both_sides_identical assert_both_sides_identical
end end
@@ -148,24 +166,64 @@ class TestWriteDuringMigration < Test::Unit::TestCase
end end
def test_many_clients_during_migration def test_many_clients_during_migration
Dir.mktmpdir() do |tmpdir| Dir.mktmpdir do |tmpdir|
Dir.chdir(tmpdir) do Dir.chdir(tmpdir) do
make_files() make_files
launch_servers() launch_servers
src_writers_1 = (1..5).collect { Thread.new { source_writer } } src_writers_1 = (1..5).collect { Thread.new { source_writer } }
start_mirror() start_mirror
src_writers_2 = (1..5).collect { Thread.new { source_writer } } src_writers_2 = (1..5).collect { Thread.new { source_writer } }
wait_for_quit() wait_for_quit
( src_writers_1 + src_writers_2 ).each {|t| t.join } (src_writers_1 + src_writers_2).each(&:join)
assert_both_sides_identical assert_both_sides_identical
end end
end end end
end end
def test_status_call_after_cleanup
Dir.mktmpdir do |tmpdir|
Dir.chdir(tmpdir) do
make_files
launch_servers
status_poker = Thread.new { bombard_with_status }
start_mirror
wait_for_quit
status_poker.join
assert_both_sides_identical
end
end
end
def test_mirroring_can_be_restarted
@size = 100 * 1024 * 1024 # 100MB
Dir.mktmpdir do |tmpdir|
Dir.chdir(tmpdir) do
make_files
launch_servers
# This is a bit racy. It needs to be slow enough that the migration
# isn't finished before the stop runs, and slow enough so that we can
# stop/start a few times.
3.times do
start_mirror
sleep 0.1
stop_mirror
sleep 0.1
end
start_mirror
wait_for_quit
end
end
end
end

View File

@@ -13,7 +13,6 @@ START_TEST( test_null_acl )
} }
END_TEST END_TEST
START_TEST(test_parses_single_line) START_TEST(test_parses_single_line)
{ {
char *lines[] = { "127.0.0.1" }; char *lines[] = { "127.0.0.1" };
@@ -37,9 +36,11 @@ START_TEST( test_parses_multiple_lines )
struct ip_and_mask *entry; struct ip_and_mask *entry;
entry = &(*acl->entries)[0]; entry = &(*acl->entries)[0];
fail_unless(entry->ip.family == e0.family, "entry 0 has wrong family!"); fail_unless(entry->ip.family == e0.family,
"entry 0 has wrong family!");
entry = &(*acl->entries)[1]; entry = &(*acl->entries)[1];
fail_unless(entry->ip.family == e1.family, "entry 1 has wrong family!"); fail_unless(entry->ip.family == e1.family,
"entry 1 has wrong family!");
} }
END_TEST END_TEST
@@ -52,7 +53,6 @@ START_TEST( test_destroy_doesnt_crash )
} }
END_TEST END_TEST
START_TEST(test_includes_single_address) START_TEST(test_includes_single_address)
{ {
char *lines[] = { "127.0.0.1" }; char *lines[] = { "127.0.0.1" };
@@ -109,12 +109,13 @@ START_TEST( test_includes_single_address_when_multiple_entries_exist )
parse_ip_to_sockaddr(&e0.generic, "127.0.0.1"); parse_ip_to_sockaddr(&e0.generic, "127.0.0.1");
parse_ip_to_sockaddr(&e1.generic, "::1"); parse_ip_to_sockaddr(&e1.generic, "::1");
fail_unless( acl_includes( acl, &e0 ), "Included address 0 wasn't covered" ); fail_unless(acl_includes(acl, &e0),
fail_unless( acl_includes( acl, &e1 ), "Included address 1 wasn't covered" ); "Included address 0 wasn't covered");
fail_unless(acl_includes(acl, &e1),
"Included address 1 wasn't covered");
} }
END_TEST END_TEST
START_TEST(test_doesnt_include_other_address) START_TEST(test_doesnt_include_other_address)
{ {
char *lines[] = { "127.0.0.1" }; char *lines[] = { "127.0.0.1" };
@@ -163,7 +164,6 @@ START_TEST( test_default_deny_rejects )
} }
END_TEST END_TEST
START_TEST(test_default_accept_rejects) START_TEST(test_default_accept_rejects)
{ {
struct acl *acl = acl_create(0, NULL, 0); struct acl *acl = acl_create(0, NULL, 0);
@@ -175,7 +175,6 @@ START_TEST( test_default_accept_rejects )
} }
END_TEST END_TEST
Suite * acl_suite(void) Suite * acl_suite(void)
{ {
Suite *s = suite_create("acl"); Suite *s = suite_create("acl");
@@ -189,14 +188,19 @@ Suite* acl_suite(void)
tcase_add_test(tc_includes, test_parses_multiple_lines); tcase_add_test(tc_includes, test_parses_multiple_lines);
tcase_add_test(tc_includes, test_includes_single_address); tcase_add_test(tc_includes, test_includes_single_address);
tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv4); tcase_add_test(tc_includes,
tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv6); test_includes_single_address_when_netmask_specified_ipv4);
tcase_add_test(tc_includes,
test_includes_single_address_when_netmask_specified_ipv6);
tcase_add_test(tc_includes, test_includes_single_address_when_multiple_entries_exist); tcase_add_test(tc_includes,
test_includes_single_address_when_multiple_entries_exist);
tcase_add_test(tc_includes, test_doesnt_include_other_address); tcase_add_test(tc_includes, test_doesnt_include_other_address);
tcase_add_test(tc_includes, test_doesnt_include_other_address_when_netmask_specified); tcase_add_test(tc_includes,
tcase_add_test(tc_includes, test_doesnt_include_other_address_when_multiple_entries_exist); test_doesnt_include_other_address_when_netmask_specified);
tcase_add_test(tc_includes,
test_doesnt_include_other_address_when_multiple_entries_exist);
tcase_add_test(tc_includes, test_default_deny_rejects); tcase_add_test(tc_includes, test_default_deny_rejects);
tcase_add_test(tc_includes, test_default_accept_rejects); tcase_add_test(tc_includes, test_default_accept_rejects);
@@ -226,4 +230,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -66,18 +66,18 @@ START_TEST(test_bit_ranges)
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
bit_set_range(buffer, i * 64, i); bit_set_range(buffer, i * 64, i);
fail_unless( fail_unless(longs[i] == (1ULL << i) - 1,
longs[i] == (1ULL<<i)-1,
"longs[%ld] = %lx SHOULD BE %lx", "longs[%ld] = %lx SHOULD BE %lx",
i, longs[i], (1ULL<<i)-1 i, longs[i], (1ULL << i) - 1);
);
fail_unless(longs[i+1] == 0, "bit_set_range overshot at i=%d", i); fail_unless(longs[i + 1] == 0, "bit_set_range overshot at i=%d",
i);
} }
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
bit_clear_range(buffer, i * 64, i); bit_clear_range(buffer, i * 64, i);
fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d", i); fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d",
i);
} }
} }
END_TEST END_TEST
@@ -86,7 +86,8 @@ START_TEST(test_bit_runs)
{ {
bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(256)]; bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(256)];
int i, ptr = 0, runs[] = { int i, ptr = 0, runs[] = {
56,97,22,12,83,1,45,80,85,51,64,40,63,67,75,64,94,81,79,62 56, 97, 22, 12, 83, 1, 45, 80, 85, 51, 64, 40, 63, 67, 75, 64, 94,
81, 79, 62
}; };
memset(buffer, 0, 256); memset(buffer, 0, 256);
@@ -101,11 +102,8 @@ START_TEST(test_bit_runs)
for (i = 0; i < 20; i += 1) { for (i = 0; i < 20; i += 1) {
int run = bit_run_count(buffer, ptr, 2048 - ptr, NULL); int run = bit_run_count(buffer, ptr, 2048 - ptr, NULL);
fail_unless( fail_unless(run == runs[i],
run == runs[i], "run %d should have been %d, was %d", i, runs[i], run);
"run %d should have been %d, was %d",
i, runs[i], run
);
ptr += runs[i]; ptr += runs[i];
} }
} }
@@ -145,7 +143,6 @@ START_TEST(test_bitset)
} }
END_TEST END_TEST
START_TEST(test_bitset_set) START_TEST(test_bitset_set)
{ {
struct bitset *map; struct bitset *map;
@@ -176,7 +173,6 @@ START_TEST( test_bitset_set )
} }
END_TEST END_TEST
START_TEST(test_bitset_clear) START_TEST(test_bitset_clear)
{ {
struct bitset *map; struct bitset *map;
@@ -443,10 +439,14 @@ START_TEST(test_bitset_stream_queued_bytes)
bitset_clear_range(map, 0, 2); bitset_clear_range(map, 0, 2);
bitset_disable_stream(map); bitset_disable_stream(map);
ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_ON ) ); ck_assert_int_eq(64,
ck_assert_int_eq( 80, bitset_stream_queued_bytes( map, BITSET_STREAM_SET ) ); bitset_stream_queued_bytes(map, BITSET_STREAM_ON));
ck_assert_int_eq( 82, bitset_stream_queued_bytes( map, BITSET_STREAM_UNSET ) ); ck_assert_int_eq(80,
ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_OFF ) ); bitset_stream_queued_bytes(map, BITSET_STREAM_SET));
ck_assert_int_eq(82,
bitset_stream_queued_bytes(map, BITSET_STREAM_UNSET));
ck_assert_int_eq(64,
bitset_stream_queued_bytes(map, BITSET_STREAM_OFF));
bitset_free(map); bitset_free(map);
} }
END_TEST END_TEST
@@ -471,7 +471,8 @@ Suite* bitset_suite(void)
tcase_add_test(tc_bitset, test_bitset_set_range); tcase_add_test(tc_bitset, test_bitset_set_range);
tcase_add_test(tc_bitset, test_bitset_clear_range); tcase_add_test(tc_bitset, test_bitset_clear_range);
tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream); tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream);
tcase_add_test(tc_bitset, test_bitset_clear_range_doesnt_push_to_stream); tcase_add_test(tc_bitset,
test_bitset_clear_range_doesnt_push_to_stream);
suite_add_tcase(s, tc_bitset); suite_add_tcase(s, tc_bitset);
@@ -497,4 +498,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -10,6 +10,7 @@
#include <unistd.h> #include <unistd.h>
struct server fake_server = { 0 }; struct server fake_server = { 0 };
#define FAKE_SERVER &fake_server #define FAKE_SERVER &fake_server
#define FAKE_SOCKET (42) #define FAKE_SOCKET (42)
@@ -23,7 +24,6 @@ START_TEST( test_assigns_socket )
} }
END_TEST END_TEST
START_TEST(test_assigns_server) START_TEST(test_assigns_server)
{ {
struct client *c; struct client *c;
@@ -37,7 +37,6 @@ START_TEST( test_assigns_server )
} }
END_TEST END_TEST
START_TEST(test_opens_stop_signal) START_TEST(test_opens_stop_signal)
{ {
struct client *c = client_create(FAKE_SERVER, FAKE_SOCKET); struct client *c = client_create(FAKE_SERVER, FAKE_SOCKET);
@@ -50,7 +49,6 @@ START_TEST( test_opens_stop_signal )
} }
END_TEST END_TEST
int fd_is_closed(int); int fd_is_closed(int);
START_TEST(test_closes_stop_signal) START_TEST(test_closes_stop_signal)
@@ -66,7 +64,6 @@ START_TEST( test_closes_stop_signal )
} }
END_TEST END_TEST
START_TEST(test_read_request_quits_on_stop_signal) START_TEST(test_read_request_quits_on_stop_signal)
{ {
int fds[2]; int fds[2];
@@ -84,7 +81,6 @@ START_TEST( test_read_request_quits_on_stop_signal )
} }
END_TEST END_TEST
Suite * client_suite(void) Suite * client_suite(void)
{ {
Suite *s = suite_create("client"); Suite *s = suite_create("client");
@@ -119,4 +115,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -15,7 +15,6 @@ START_TEST( test_assigns_sock_name )
} }
END_TEST END_TEST
Suite * control_suite(void) Suite * control_suite(void)
{ {
Suite *s = suite_create("control"); Suite *s = suite_create("control");
@@ -39,4 +38,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -5,8 +5,7 @@
START_TEST(test_listening_assigns_sock) START_TEST(test_listening_assigns_sock)
{ {
struct flexnbd * flexnbd = flexnbd_create_listening( struct flexnbd *flexnbd = flexnbd_create_listening("127.0.0.1",
"127.0.0.1",
"4777", "4777",
"fakefile", "fakefile",
"fakesock", "fakesock",
@@ -17,7 +16,6 @@ START_TEST( test_listening_assigns_sock )
} }
END_TEST END_TEST
Suite * flexnbd_suite(void) Suite * flexnbd_suite(void)
{ {
Suite *s = suite_create("flexnbd"); Suite *s = suite_create("flexnbd");
@@ -41,4 +39,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -12,22 +12,23 @@ START_TEST( test_mutex_create )
} }
END_TEST END_TEST
START_TEST(test_mutex_lock) START_TEST(test_mutex_lock)
{ {
struct flexthread_mutex *ftm = flexthread_mutex_create(); struct flexthread_mutex *ftm = flexthread_mutex_create();
fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held before lock" ); fail_if(flexthread_mutex_held(ftm),
"Flexthread_mutex is held before lock");
flexthread_mutex_lock(ftm); flexthread_mutex_lock(ftm);
fail_unless( flexthread_mutex_held( ftm ), "Flexthread_mutex is not held inside lock" ); fail_unless(flexthread_mutex_held(ftm),
"Flexthread_mutex is not held inside lock");
flexthread_mutex_unlock(ftm); flexthread_mutex_unlock(ftm);
fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held after unlock" ); fail_if(flexthread_mutex_held(ftm),
"Flexthread_mutex is held after unlock");
flexthread_mutex_destroy(ftm); flexthread_mutex_destroy(ftm);
} }
END_TEST END_TEST
Suite * flexthread_suite(void) Suite * flexthread_suite(void)
{ {
Suite *s = suite_create("flexthread"); Suite *s = suite_create("flexthread");
@@ -59,4 +60,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -17,7 +17,6 @@ START_TEST( test_read_until_newline_returns_line_length_plus_null )
} }
END_TEST END_TEST
START_TEST(test_read_until_newline_inserts_null) START_TEST(test_read_until_newline_inserts_null)
{ {
int fds[2]; int fds[2];
@@ -34,7 +33,6 @@ START_TEST( test_read_until_newline_inserts_null )
} }
END_TEST END_TEST
START_TEST(test_read_empty_line_inserts_null) START_TEST(test_read_empty_line_inserts_null)
{ {
int fds[2]; int fds[2];
@@ -50,7 +48,6 @@ START_TEST( test_read_empty_line_inserts_null )
} }
END_TEST END_TEST
START_TEST(test_read_eof_returns_err) START_TEST(test_read_eof_returns_err)
{ {
int fds[2]; int fds[2];
@@ -65,7 +62,6 @@ START_TEST( test_read_eof_returns_err )
} }
END_TEST END_TEST
START_TEST(test_read_eof_fills_line) START_TEST(test_read_eof_fills_line)
{ {
int fds[2]; int fds[2];
@@ -82,7 +78,6 @@ START_TEST( test_read_eof_fills_line )
} }
END_TEST END_TEST
START_TEST(test_read_lines_until_blankline) START_TEST(test_read_lines_until_blankline)
{ {
char **lines = NULL; char **lines = NULL;
@@ -98,21 +93,25 @@ START_TEST( test_read_lines_until_blankline )
} }
END_TEST END_TEST
Suite * ioutil_suite(void) Suite * ioutil_suite(void)
{ {
Suite *s = suite_create("ioutil"); Suite *s = suite_create("ioutil");
TCase *tc_read_until_newline = tcase_create("read_until_newline"); TCase *tc_read_until_newline = tcase_create("read_until_newline");
TCase *tc_read_lines_until_blankline = tcase_create("read_lines_until_blankline"); TCase *tc_read_lines_until_blankline =
tcase_create("read_lines_until_blankline");
tcase_add_test(tc_read_until_newline, test_read_until_newline_returns_line_length_plus_null); tcase_add_test(tc_read_until_newline,
tcase_add_test(tc_read_until_newline, test_read_until_newline_inserts_null); test_read_until_newline_returns_line_length_plus_null);
tcase_add_test(tc_read_until_newline, test_read_empty_line_inserts_null); tcase_add_test(tc_read_until_newline,
test_read_until_newline_inserts_null);
tcase_add_test(tc_read_until_newline,
test_read_empty_line_inserts_null);
tcase_add_test(tc_read_until_newline, test_read_eof_returns_err); tcase_add_test(tc_read_until_newline, test_read_eof_returns_err);
tcase_add_test(tc_read_until_newline, test_read_eof_fills_line); tcase_add_test(tc_read_until_newline, test_read_eof_fills_line);
tcase_add_test(tc_read_lines_until_blankline, test_read_lines_until_blankline ); tcase_add_test(tc_read_lines_until_blankline,
test_read_lines_until_blankline);
suite_add_tcase(s, tc_read_until_newline); suite_add_tcase(s, tc_read_until_newline);
suite_add_tcase(s, tc_read_lines_until_blankline); suite_add_tcase(s, tc_read_lines_until_blankline);
@@ -131,4 +130,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -14,12 +14,11 @@ START_TEST( test_allocs_cvar )
memset(&cond_zero, 'X', sizeof(cond_zero)); memset(&cond_zero, 'X', sizeof(cond_zero));
fail_if(memcmp(&cond_zero, &mbox->filled_cond, sizeof(cond_zero)) == 0, fail_if(memcmp(&cond_zero, &mbox->filled_cond, sizeof(cond_zero)) == 0,
"Condition variable not allocated"); "Condition variable not allocated");
fail_if( memcmp( &cond_zero, &mbox->emptied_cond, sizeof( cond_zero ) ) == 0 , fail_if(memcmp(&cond_zero, &mbox->emptied_cond, sizeof(cond_zero)) ==
"Condition variable not allocated" ); 0, "Condition variable not allocated");
} }
END_TEST END_TEST
START_TEST(test_post_stores_value) START_TEST(test_post_stores_value)
{ {
struct mbox *mbox = mbox_create(); struct mbox *mbox = mbox_create();
@@ -32,7 +31,6 @@ START_TEST( test_post_stores_value )
} }
END_TEST END_TEST
void *mbox_receive_runner(void *mbox_uncast) void *mbox_receive_runner(void *mbox_uncast)
{ {
struct mbox *mbox = (struct mbox *) mbox_uncast; struct mbox *mbox = (struct mbox *) mbox_uncast;
@@ -58,14 +56,12 @@ START_TEST( test_receive_blocks_until_post )
mbox_post(mbox, deadbeef); mbox_post(mbox, deadbeef);
fail_unless(0 == pthread_join(receiver, &retval), fail_unless(0 == pthread_join(receiver, &retval),
"Failed to join the receiver thread"); "Failed to join the receiver thread");
fail_unless( retval == deadbeef, fail_unless(retval == deadbeef, "Return value was wrong");
"Return value was wrong" );
} }
END_TEST END_TEST
Suite * mbox_suite(void) Suite * mbox_suite(void)
{ {
Suite *s = suite_create("mbox"); Suite *s = suite_create("mbox");
@@ -101,4 +97,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -13,12 +13,13 @@ START_TEST(test_init_passwd)
memset(init_raw.passwd, 0, 8); memset(init_raw.passwd, 0, 8);
nbd_h2r_init(&init, &init_raw); nbd_h2r_init(&init, &init_raw);
fail_unless( memcmp( init.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied." ); fail_unless(memcmp(init.passwd, INIT_PASSWD, 8) == 0,
fail_unless( memcmp( init_raw.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied back." ); "The password was not copied.");
fail_unless(memcmp(init_raw.passwd, INIT_PASSWD, 8) == 0,
"The password was not copied back.");
} }
END_TEST END_TEST
START_TEST(test_init_magic) START_TEST(test_init_magic)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
@@ -30,11 +31,11 @@ START_TEST(test_init_magic)
init.magic = 67890; init.magic = 67890;
nbd_h2r_init(&init, &init_raw); nbd_h2r_init(&init, &init_raw);
fail_unless( htobe64( 67890 ) == init_raw.magic, "Magic was not converted back." ); fail_unless(htobe64(67890) == init_raw.magic,
"Magic was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_init_size) START_TEST(test_init_size)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
@@ -46,11 +47,11 @@ START_TEST(test_init_size)
init.size = 67890; init.size = 67890;
nbd_h2r_init(&init, &init_raw); nbd_h2r_init(&init, &init_raw);
fail_unless( htobe64( 67890 ) == init_raw.size, "Size was not converted back." ); fail_unless(htobe64(67890) == init_raw.size,
"Size was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_request_magic) START_TEST(test_request_magic)
{ {
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
@@ -58,11 +59,13 @@ START_TEST(test_request_magic )
request_raw.magic = 12345; request_raw.magic = 12345;
nbd_r2h_request(&request_raw, &request); nbd_r2h_request(&request_raw, &request);
fail_unless( be32toh( 12345 ) == request.magic, "Magic was not converted." ); fail_unless(be32toh(12345) == request.magic,
"Magic was not converted.");
request.magic = 67890; request.magic = 67890;
nbd_h2r_request(&request, &request_raw); nbd_h2r_request(&request, &request_raw);
fail_unless( htobe32( 67890 ) == request_raw.magic, "Magic was not converted back." ); fail_unless(htobe32(67890) == request_raw.magic,
"Magic was not converted back.");
} }
END_TEST END_TEST
@@ -71,17 +74,33 @@ START_TEST(test_request_type )
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
struct nbd_request request; struct nbd_request request;
request_raw.type = 12345; request_raw.type = 123;
nbd_r2h_request(&request_raw, &request); nbd_r2h_request(&request_raw, &request);
fail_unless( be32toh( 12345 ) == request.type, "Type was not converted." ); fail_unless(be16toh(123) == request.type, "Type was not converted.");
request.type = 67890; request.type = 234;
nbd_h2r_request(&request, &request_raw); nbd_h2r_request(&request, &request_raw);
fail_unless( htobe32( 67890 ) == request_raw.type, "Type was not converted back." ); fail_unless(htobe16(234) == request_raw.type,
"Type was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_request_flags)
{
struct nbd_request_raw request_raw;
struct nbd_request request;
request_raw.flags = 123;
nbd_r2h_request(&request_raw, &request);
fail_unless(be16toh(123) == request.flags,
"Flags were not converted.");
request.flags = 234;
nbd_h2r_request(&request, &request_raw);
fail_unless(htobe16(234) == request_raw.flags,
"Flags were not converted back.");
}
END_TEST
START_TEST(test_request_handle) START_TEST(test_request_handle)
{ {
@@ -94,13 +113,13 @@ START_TEST(test_request_handle)
request_raw.handle.w = 0; request_raw.handle.w = 0;
nbd_h2r_request(&request, &request_raw); nbd_h2r_request(&request, &request_raw);
fail_unless( memcmp( request.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied." ); fail_unless(memcmp(request.handle.b, "MYHANDLE", 8) == 0,
fail_unless( memcmp( request_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." ); "The handle was not copied.");
fail_unless(memcmp(request_raw.handle.b, "MYHANDLE", 8) == 0,
"The handle was not copied back.");
} }
END_TEST END_TEST
START_TEST(test_request_from) START_TEST(test_request_from)
{ {
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
@@ -112,12 +131,11 @@ START_TEST(test_request_from )
request.from = 67890; request.from = 67890;
nbd_h2r_request(&request, &request_raw); nbd_h2r_request(&request, &request_raw);
fail_unless( htobe64( 67890 ) == request_raw.from, "From was not converted back." ); fail_unless(htobe64(67890) == request_raw.from,
"From was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_request_len) START_TEST(test_request_len)
{ {
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
@@ -129,11 +147,11 @@ START_TEST(test_request_len )
request.len = 67890; request.len = 67890;
nbd_h2r_request(&request, &request_raw); nbd_h2r_request(&request, &request_raw);
fail_unless( htobe32( 67890 ) == request_raw.len, "Type was not converted back." ); fail_unless(htobe32(67890) == request_raw.len,
"Type was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_reply_magic) START_TEST(test_reply_magic)
{ {
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
@@ -145,11 +163,11 @@ START_TEST(test_reply_magic )
reply.magic = 67890; reply.magic = 67890;
nbd_h2r_reply(&reply, &reply_raw); nbd_h2r_reply(&reply, &reply_raw);
fail_unless( htobe32( 67890 ) == reply_raw.magic, "Magic was not converted back." ); fail_unless(htobe32(67890) == reply_raw.magic,
"Magic was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_reply_error) START_TEST(test_reply_error)
{ {
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
@@ -161,7 +179,8 @@ START_TEST(test_reply_error )
reply.error = 67890; reply.error = 67890;
nbd_h2r_reply(&reply, &reply_raw); nbd_h2r_reply(&reply, &reply_raw);
fail_unless( htobe32( 67890 ) == reply_raw.error, "Error was not converted back." ); fail_unless(htobe32(67890) == reply_raw.error,
"Error was not converted back.");
} }
END_TEST END_TEST
@@ -176,12 +195,13 @@ START_TEST(test_reply_handle)
reply_raw.handle.w = 0; reply_raw.handle.w = 0;
nbd_h2r_reply(&reply, &reply_raw); nbd_h2r_reply(&reply, &reply_raw);
fail_unless( memcmp( reply.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied." ); fail_unless(memcmp(reply.handle.b, "MYHANDLE", 8) == 0,
fail_unless( memcmp( reply_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." ); "The handle was not copied.");
fail_unless(memcmp(reply_raw.handle.b, "MYHANDLE", 8) == 0,
"The handle was not copied back.");
} }
END_TEST END_TEST
START_TEST(test_convert_from) START_TEST(test_convert_from)
{ {
/* Check that we can correctly pull numbers out of an /* Check that we can correctly pull numbers out of an
@@ -213,6 +233,7 @@ Suite *nbdtypes_suite(void)
tcase_add_test(tc_init, test_init_size); tcase_add_test(tc_init, test_init_size);
tcase_add_test(tc_request, test_request_magic); tcase_add_test(tc_request, test_request_magic);
tcase_add_test(tc_request, test_request_type); tcase_add_test(tc_request, test_request_type);
tcase_add_test(tc_request, test_request_flags);
tcase_add_test(tc_request, test_request_handle); tcase_add_test(tc_request, test_request_handle);
tcase_add_test(tc_request, test_request_from); tcase_add_test(tc_request, test_request_from);
tcase_add_test(tc_request, test_request_len); tcase_add_test(tc_request, test_request_len);
@@ -240,4 +261,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -13,8 +13,6 @@ START_TEST( test_can_parse_ip_address_twice )
} }
END_TEST END_TEST
Suite * parse_suite(void) Suite * parse_suite(void)
{ {
Suite *s = suite_create("parse"); Suite *s = suite_create("parse");
@@ -44,4 +42,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -54,8 +54,7 @@ void * responder( void *respond_uncast )
nbd_r2h_request(&request_raw, &resp->received); nbd_r2h_request(&request_raw, &resp->received);
if (resp->do_fail) { if (resp->do_fail) {
fd_write_reply(sock_fd, wrong_handle, 0); fd_write_reply(sock_fd, wrong_handle, 0);
} } else {
else {
fd_write_reply(sock_fd, resp->received.handle.w, 0); fd_write_reply(sock_fd, resp->received.handle.w, 0);
} }
write(sock_fd, "12345678", 8); write(sock_fd, "12345678", 8);
@@ -66,17 +65,20 @@ void * responder( void *respond_uncast )
struct respond *respond_create(int do_fail) struct respond *respond_create(int do_fail)
{ {
struct respond * respond = (struct respond *)calloc( 1, sizeof( struct respond ) ); struct respond *respond =
(struct respond *) calloc(1, sizeof(struct respond));
socketpair(PF_UNIX, SOCK_STREAM, 0, respond->sock_fds); socketpair(PF_UNIX, SOCK_STREAM, 0, respond->sock_fds);
respond->do_fail = do_fail; respond->do_fail = do_fail;
pthread_attr_init(&respond->thread_attr); pthread_attr_init(&respond->thread_attr);
pthread_create( &respond->thread_id, &respond->thread_attr, responder, respond ); pthread_create(&respond->thread_id, &respond->thread_attr, responder,
respond);
return respond; return respond;
} }
void respond_destroy( struct respond * respond ){ void respond_destroy(struct respond *respond)
{
NULLCHECK(respond); NULLCHECK(respond);
pthread_join(respond->thread_id, NULL); pthread_join(respond->thread_id, NULL);
@@ -121,7 +123,6 @@ START_TEST( test_rejects_mismatched_handle )
} }
END_TEST END_TEST
START_TEST(test_accepts_matched_handle) START_TEST(test_accepts_matched_handle)
{ {
struct respond *respond = respond_create(0); struct respond *respond = respond_create(0);
@@ -135,7 +136,6 @@ START_TEST( test_accepts_matched_handle )
} }
END_TEST END_TEST
START_TEST(test_disconnect_doesnt_read_reply) START_TEST(test_disconnect_doesnt_read_reply)
{ {
struct respond *respond = respond_create(1); struct respond *respond = respond_create(1);
@@ -146,7 +146,6 @@ START_TEST( test_disconnect_doesnt_read_reply )
} }
END_TEST END_TEST
Suite * readwrite_suite(void) Suite * readwrite_suite(void)
{ {
Suite *s = suite_create("readwrite"); Suite *s = suite_create("readwrite");
@@ -162,7 +161,8 @@ Suite* readwrite_suite(void)
* because we want to know that the sender won't even try to * because we want to know that the sender won't even try to
* read the response. * read the response.
*/ */
tcase_add_exit_test( tc_disconnect, test_disconnect_doesnt_read_reply,0 ); tcase_add_exit_test(tc_disconnect, test_disconnect_doesnt_read_reply,
0);
suite_add_tcase(s, tc_transfer); suite_add_tcase(s, tc_transfer);
suite_add_tcase(s, tc_disconnect); suite_add_tcase(s, tc_disconnect);
@@ -190,4 +190,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -24,7 +24,6 @@ START_TEST( test_opens_pipe )
} }
END_TEST END_TEST
void *signal_thread(void *thing) void *signal_thread(void *thing)
{ {
struct self_pipe *sig = (struct self_pipe *) thing; struct self_pipe *sig = (struct self_pipe *) thing;
@@ -63,14 +62,14 @@ START_TEST( test_signals )
} }
self_pipe_signal_clear(sig); self_pipe_signal_clear(sig);
fail_unless( self_pipe_fd_isset( sig, &fds ), "Signalled pipe was not FD_ISSET." ); fail_unless(self_pipe_fd_isset(sig, &fds),
"Signalled pipe was not FD_ISSET.");
pthread_join(signal_thread_id, NULL); pthread_join(signal_thread_id, NULL);
self_pipe_destroy(sig); self_pipe_destroy(sig);
} }
END_TEST END_TEST
START_TEST(test_clear_returns_immediately) START_TEST(test_clear_returns_immediately)
{ {
struct self_pipe *sig; struct self_pipe *sig;
@@ -79,7 +78,6 @@ START_TEST( test_clear_returns_immediately )
} }
END_TEST END_TEST
START_TEST(test_destroy_closes_read_pipe) START_TEST(test_destroy_closes_read_pipe)
{ {
struct self_pipe *sig; struct self_pipe *sig;
@@ -116,7 +114,6 @@ START_TEST( test_destroy_closes_read_pipe )
} }
END_TEST END_TEST
START_TEST(test_destroy_closes_write_pipe) START_TEST(test_destroy_closes_write_pipe)
{ {
struct self_pipe *sig; struct self_pipe *sig;
@@ -127,7 +124,8 @@ START_TEST( test_destroy_closes_write_pipe )
orig_write_fd = sig->write_fd; orig_write_fd = sig->write_fd;
self_pipe_destroy(sig); self_pipe_destroy(sig);
while ( ( write_len = write( orig_write_fd, "", 0 ) ) == -1 && errno == EINTR ); while ((write_len = write(orig_write_fd, "", 0)) == -1
&& errno == EINTR);
switch (write_len) { switch (write_len) {
case 0: case 0:
@@ -158,8 +156,6 @@ START_TEST( test_destroy_closes_write_pipe )
} }
END_TEST END_TEST
Suite * self_pipe_suite(void) Suite * self_pipe_suite(void)
{ {
Suite *s = suite_create("self_pipe"); Suite *s = suite_create("self_pipe");
@@ -195,4 +191,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -54,7 +54,9 @@ void setup( void )
void teardown(void) void teardown(void)
{ {
if( dummy_file ){ unlink( dummy_file ); } if (dummy_file) {
unlink(dummy_file);
}
free(dummy_file); free(dummy_file);
dummy_file = NULL; dummy_file = NULL;
} }
@@ -64,7 +66,9 @@ START_TEST( test_replaces_acl )
{ {
struct flexnbd flexnbd; struct flexnbd flexnbd;
flexnbd.signal_fd = -1; flexnbd.signal_fd = -1;
struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct server *s =
server_create(&flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL,
1, 0, 1);
struct acl *new_acl = acl_create(0, NULL, 0); struct acl *new_acl = acl_create(0, NULL, 0);
server_replace_acl(s, new_acl); server_replace_acl(s, new_acl);
@@ -74,12 +78,13 @@ START_TEST( test_replaces_acl )
} }
END_TEST END_TEST
START_TEST(test_signals_acl_updated) START_TEST(test_signals_acl_updated)
{ {
struct flexnbd flexnbd; struct flexnbd flexnbd;
flexnbd.signal_fd = -1; flexnbd.signal_fd = -1;
struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct server *s =
server_create(&flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL,
1, 0, 1);
struct acl *new_acl = acl_create(0, NULL, 0); struct acl *new_acl = acl_create(0, NULL, 0);
server_replace_acl(s, new_acl); server_replace_acl(s, new_acl);
@@ -90,7 +95,6 @@ START_TEST( test_signals_acl_updated )
} }
END_TEST END_TEST
int connect_client(char *addr, int actual_port, char *source_addr) int connect_client(char *addr, int actual_port, char *source_addr)
{ {
int client_fd = -1; int client_fd = -1;
@@ -104,12 +108,15 @@ int connect_client( char *addr, int actual_port, char *source_addr )
memset(&hint, '\0', sizeof(struct addrinfo)); memset(&hint, '\0', sizeof(struct addrinfo));
hint.ai_socktype = SOCK_STREAM; hint.ai_socktype = SOCK_STREAM;
myfail_if( getaddrinfo( addr, NULL, &hint, &ailist ) != 0, "getaddrinfo failed." ); myfail_if(getaddrinfo(addr, NULL, &hint, &ailist) != 0,
"getaddrinfo failed.");
int connected = 0; int connected = 0;
for (aip = ailist; aip; aip = aip->ai_next) { for (aip = ailist; aip; aip = aip->ai_next) {
((struct sockaddr_in *)aip->ai_addr)->sin_port = htons( actual_port ); ((struct sockaddr_in *) aip->ai_addr)->sin_port =
client_fd = socket( aip->ai_family, aip->ai_socktype, aip->ai_protocol ); htons(actual_port);
client_fd =
socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
if (source_addr) { if (source_addr) {
struct sockaddr src; struct sockaddr src;
@@ -120,7 +127,9 @@ int connect_client( char *addr, int actual_port, char *source_addr )
bind(client_fd, &src, sizeof(struct sockaddr_in6)); bind(client_fd, &src, sizeof(struct sockaddr_in6));
} }
if( client_fd == -1) { continue; } if (client_fd == -1) {
continue;
}
if (connect(client_fd, aip->ai_addr, aip->ai_addrlen) == 0) { if (connect(client_fd, aip->ai_addr, aip->ai_addrlen) == 0) {
connected = 1; connected = 1;
break; break;
@@ -148,7 +157,9 @@ START_TEST( test_acl_update_closes_bad_client )
*/ */
struct flexnbd flexnbd; struct flexnbd flexnbd;
flexnbd.signal_fd = -1; flexnbd.signal_fd = -1;
struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct server *s =
server_create(&flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL,
1, 0, 1);
struct acl *new_acl = acl_create(0, NULL, 1); struct acl *new_acl = acl_create(0, NULL, 1);
struct client *c; struct client *c;
struct client_tbl_entry *entry; struct client_tbl_entry *entry;
@@ -187,13 +198,14 @@ START_TEST( test_acl_update_closes_bad_client )
} }
END_TEST END_TEST
START_TEST(test_acl_update_leaves_good_client) START_TEST(test_acl_update_leaves_good_client)
{ {
struct flexnbd flexnbd; struct flexnbd flexnbd;
flexnbd.signal_fd = -1; flexnbd.signal_fd = -1;
struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 ); struct server *s =
server_create(&flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL,
1, 0, 1);
char *lines[] = { "127.0.0.1" }; char *lines[] = { "127.0.0.1" };
struct acl *new_acl = acl_create(1, lines, 1); struct acl *new_acl = acl_create(1, lines, 1);
@@ -230,7 +242,6 @@ START_TEST( test_acl_update_leaves_good_client )
} }
END_TEST END_TEST
Suite * serve_suite(void) Suite * serve_suite(void)
{ {
Suite *s = suite_create("serve"); Suite *s = suite_create("serve");
@@ -241,8 +252,10 @@ Suite* serve_suite(void)
tcase_add_test(tc_acl_update, test_replaces_acl); tcase_add_test(tc_acl_update, test_replaces_acl);
tcase_add_test(tc_acl_update, test_signals_acl_updated); tcase_add_test(tc_acl_update, test_signals_acl_updated);
tcase_add_exit_test(tc_acl_update, test_acl_update_closes_bad_client, 0); tcase_add_exit_test(tc_acl_update, test_acl_update_closes_bad_client,
tcase_add_exit_test(tc_acl_update, test_acl_update_leaves_good_client, 0); 0);
tcase_add_exit_test(tc_acl_update, test_acl_update_leaves_good_client,
0);
suite_add_tcase(s, tc_acl_update); suite_add_tcase(s, tc_acl_update);
@@ -261,4 +274,3 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

Some files were not shown because too many files have changed in this diff Show More