1/* Example plugin. 2 * Copyright (C) 2013-2020 Red Hat Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * * Neither the name of Red Hat nor the names of its contributors may be 16 * used to endorse or promote products derived from this software without 17 * specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33package main 34 35import ( 36 "C" 37 "io/ioutil" 38 "libguestfs.org/nbdkit" 39 "os" 40 "strconv" 41 "unsafe" 42) 43 44var pluginName = "disk" 45 46// The plugin global struct. 47type DiskPlugin struct { 48 nbdkit.Plugin 49} 50 51// The per-client struct. 52type DiskConnection struct { 53 nbdkit.Connection 54 fd *os.File // Per-client temporary disk. 55} 56 57var size uint64 58var size_set = false 59 60// Parse the size parameter on the command line. 61func (p *DiskPlugin) Config(key string, value string) error { 62 if key == "size" { 63 var err error 64 size, err = strconv.ParseUint(value, 0, 64) 65 if err != nil { 66 return err 67 } 68 size_set = true 69 return nil 70 } else { 71 return nbdkit.PluginError{Errmsg: "unknown parameter"} 72 } 73} 74 75// Make sure the user specified the size parameter. 76func (p *DiskPlugin) ConfigComplete() error { 77 if !size_set { 78 return nbdkit.PluginError{Errmsg: "size parameter is required"} 79 } 80 return nil 81} 82 83func (p *DiskPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) { 84 // Open a temporary file. 85 fd, err := ioutil.TempFile("/var/tmp", "nbdkitdisk") 86 if err != nil { 87 return nil, err 88 } 89 os.Remove(fd.Name()) 90 91 // Truncate it to the right size. 92 err = fd.Truncate(int64(size)) 93 if err != nil { 94 return nil, err 95 } 96 97 // Store the file descriptor of the temporary file in the 98 // Connection struct. 99 return &DiskConnection{fd: fd}, nil 100} 101 102func (c *DiskConnection) Close() { 103 c.fd.Close() 104} 105 106// Return the size of the disk. We could just return the global 107// "size" here, but make the example more interesting. 108func (c *DiskConnection) GetSize() (uint64, error) { 109 info, err := c.fd.Stat() 110 if err != nil { 111 return 0, err 112 } 113 return uint64(info.Size()), nil 114} 115 116// Multi-conn is NOT safe because each client sees a different disk. 117func (c *DiskConnection) CanMultiConn() (bool, error) { 118 return false, nil 119} 120 121func (c *DiskConnection) PRead(buf []byte, offset uint64, 122 flags uint32) error { 123 n, err := c.fd.ReadAt(buf, int64(offset)) 124 if err != nil { 125 return err 126 } 127 // NBD requests must always read/write the whole requested 128 // amount, or else fail. Actually we should loop here (XXX). 129 if n != len(buf) { 130 return nbdkit.PluginError{Errmsg: "short read"} 131 } 132 return nil 133} 134 135// Note that CanWrite() is required in golang plugins that implement 136// PWrite(), otherwise PWrite() will never be called. 137func (c *DiskConnection) CanWrite() (bool, error) { 138 return true, nil 139} 140 141func (c *DiskConnection) PWrite(buf []byte, offset uint64, 142 flags uint32) error { 143 n, err := c.fd.WriteAt(buf, int64(offset)) 144 if err != nil { 145 return err 146 } 147 // NBD requests must always read/write the whole requested 148 // amount, or else fail. Actually we should loop here (XXX). 149 if n != len(buf) { 150 return nbdkit.PluginError{Errmsg: "short write"} 151 } 152 return nil 153} 154 155// Note that CanFlush() is required in golang plugins that implement 156// Flush(), otherwise Flush() will never be called. 157func (c *DiskConnection) CanFlush() (bool, error) { 158 return true, nil 159} 160 161// This is only an example, but if this was a real plugin, because 162// these disks are transient and deleted when the client exits, it 163// would make no sense to implement a Flush() callback. 164func (c *DiskConnection) Flush(flags uint32) error { 165 return c.fd.Sync() 166} 167 168//---------------------------------------------------------------------- 169// 170// The boilerplate below this line is required by all golang plugins, 171// as well as importing "C" and "unsafe" modules at the top of the 172// file. 173 174//export plugin_init 175func plugin_init() unsafe.Pointer { 176 // If your plugin needs to do any initialization, you can 177 // either put it here or implement a Load() method. 178 // ... 179 180 // Then you must call the following function. 181 return nbdkit.PluginInitialize(pluginName, &DiskPlugin{}) 182} 183 184// This is never(?) called, but must exist. 185func main() {} 186