5

I'm doing mmap() on a file that can be unmounted ( the file is located on an USB device which the user can remove at any time ), and my application crashes if the file is unmounted and then i try to access any element in the buffer.

Any solutions to this ?

Cumatru
  • 695
  • 2
  • 12
  • 34

4 Answers4

8

First of all, I would like to say this should serve as a good argument not to use mmap unnecessarily as an "optimized read" or similar. Aside from device removal, issues like file truncation by other processes can cause accesses to fault with SIGBUS.

If you do really need to use mmap, you could install a signal handler for SIGBUS. Its task should basically be to:

  1. Set a global (or thread-local, if your program is multi-threaded) flag that a SIGBUS occurred, so the faulting code can be aware.
  2. Call mmap with MAP_FIXED to map a new anonymous page over top of the faulting page. Optionally fill it with data which will be recognized by the code accessing the map as erroneous; this could make step 1 unnecessary.

An alternative approach would be to set a global (or thread-local) jmp_buf before accessing the map, and have the signal handler simply call longjmp.

Note that neither mmap nor longjmp is async-signal-safe, but the SIGBUS in question is not an asynchronous signal (although it should perhaps be considered one if the faulting access happened inside a non-async-signal-safe library function such as sscanf). As long as it's your own code, and not library functions, accessing the map, you should be safe with either. And mmap is async-signal-safe in most/all real-world implementations, so you should be okay with the first solution in practice even if it's not formally correct.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    Would phrase it like that: there's more to `mmap` than meets the eye - because error handling is far more complex than for traditional I/O. That's not necessarily a reason _against using_`mmap` ... just be aware of the spirits you summon ;-) – FrankH. Oct 16 '12 at 12:31
  • And make sure they don't fly out of your nose.. :-) – R.. GitHub STOP HELPING ICE Oct 16 '12 at 13:31
3

The simplest thing is to set up a signal handler that will check for accesses to memory locations that correspond to mmaped addresses.

You would use the sigaction form of signal handlers, rather than the simpler signal handlers as sigaction handlers receive information in the struct __siginfo * parameter corresponding to the address of the signal. This can be checked to see if it is within the address range of the mmaped file.

mmap is great when you don't want to deal with the complications of buffer reading/writing of data, but you only get one form of error (a signal) due to something going wrong. with the read/write mechanism, you can get the errno and determine what happened. It is very much a developer choice in this case.

To jump to a location after receiving the signal then you will need to make use of the setjmp and longjmp/siglongjmp - see some use of this in this question

Community
  • 1
  • 1
Anya Shenanigans
  • 91,618
  • 3
  • 107
  • 122
-2

Do not access a file that is not available. Check whether the file is still there, or use a file which cannot be unmounted.

swegi
  • 4,046
  • 1
  • 26
  • 45
  • 4
    I don't think that's very helpful due to possible race conditions. Would it help to expect and catch `SIGBUS` around the code accessing the mapped region? Or something similar? – Alexey Frunze Oct 15 '12 at 10:35
  • 1
    No. `SIGBUS` is not necessarily recoverable, although some implementations retry the faulting instruction after return from the handler. – Simon Richter Oct 15 '12 at 10:53
  • @SimonRichter You don't need to retry the faulting instruction, you only need to determine you can't proceed with a read/write, after which you cease attempts to access the region and return to the caller with some error indication. Perhaps `setjmp()`/`longjmp()` would be able to avoid reexecution of the faulting instruction. – Alexey Frunze Oct 15 '12 at 11:28
  • 1
    @SimonRichter: SIGBUS is recoverable. You can either `longjmp` out (could be messy), or (much easier) `mmap` with `MAP_FIXED` a new anonymous map over top of the faulting page. – R.. GitHub STOP HELPING ICE Oct 15 '12 at 13:08
  • @R.. not on all platforms. Many do retry the faulting instruction on return from the handler, which allows creating a new mapping as you suggest, but that is not an universal guarantee. – Simon Richter Oct 15 '13 at 09:25
-2

you can get notification about any change on file(s), dir(s) by using http://linux.die.net/man/7/inotify. you may consider using IN_DELETE.

sardok
  • 1,086
  • 1
  • 10
  • 19
  • 4
    Will those notifications will be as immediate as page faults that the OP is trying to curb? If not, don't bother. – Alexey Frunze Oct 15 '12 at 13:36
  • It appears that you have to call `read()` to find out if there have been any changes, but the changes may happen between `read()` returning nothing of interest and the following memory access. It's a race condition. Also, the event queue may overflow. So, this isn't a solution. – Alexey Frunze Oct 15 '12 at 13:44