I wouldn't recommend #3. There are significant tricky issues about arbitrating for the media to allow both ends to access it. Also writing files is significantly difficult, reading them is none to easy. A Mass storage device only needs to know about reading and writing blocks, it doesn't know anything about what files are in those blocks. I have used this method, we use it for the host to update the device, the device has to be able to find a single file and read the contents, not a very difficult thing, but significantly more difficult than just being a block device. We also send files back the host using virtual files. I write directory entries for blocks which are beyond the end of the physical media, when requests are made for those blocks I get the needed data from somewhere else.
#2 may be doable. I've not made a composite MSC/CDC device yet, I've been avoiding that. Our device is either MSC or CDC depending on how the user plugs it in. Do you actually need the MSC portion? Could you do it all via CDC? CDC is about the simplest protocol you can use.
#1 is relatively easy. I've done this by implementing both custom device requests and custom SCSI commands. If you already have a working MSC device (the linux gadget), then this should be pretty trivial to impliment on the device. I've never done the Windows end of this, that may be the trickiest part. Its pretty trivial to access the device from a Mac host.