github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/mount_unix.go (about) 1 // +build linux darwin freebsd 2 // +build !nofuse 3 4 package commands 5 6 import ( 7 "fmt" 8 "io" 9 "strings" 10 "time" 11 12 cmds "github.com/ipfs/go-ipfs/commands" 13 core "github.com/ipfs/go-ipfs/core" 14 ipns "github.com/ipfs/go-ipfs/fuse/ipns" 15 mount "github.com/ipfs/go-ipfs/fuse/mount" 16 rofs "github.com/ipfs/go-ipfs/fuse/readonly" 17 config "github.com/ipfs/go-ipfs/repo/config" 18 ) 19 20 // amount of time to wait for mount errors 21 // TODO is this non-deterministic? 22 const mountTimeout = time.Second 23 24 // fuseNoDirectory used to check the returning fuse error 25 const fuseNoDirectory = "fusermount: failed to access mountpoint" 26 27 // fuseExitStatus1 used to check the returning fuse error 28 const fuseExitStatus1 = "fusermount: exit status 1" 29 30 // platformFuseChecks can get overridden by arch-specific files 31 // to run fuse checks (like checking the OSXFUSE version) 32 var platformFuseChecks = func(*core.IpfsNode) error { 33 return nil 34 } 35 36 var MountCmd = &cmds.Command{ 37 Helptext: cmds.HelpText{ 38 Tagline: "Mounts IPFS to the filesystem (read-only)", 39 Synopsis: ` 40 ipfs mount [-f <ipfs mount path>] [-n <ipns mount path>] 41 `, 42 ShortDescription: ` 43 Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). 44 All ipfs objects will be accessible under that directory. Note that the 45 root will not be listable, as it is virtual. Access known paths directly. 46 47 You may have to create /ipfs and /ipns before using 'ipfs mount': 48 49 > sudo mkdir /ipfs /ipns 50 > sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns 51 > ipfs daemon & 52 > ipfs mount 53 `, 54 LongDescription: ` 55 Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). 56 All ipfs objects will be accessible under that directory. Note that the 57 root will not be listable, as it is virtual. Access known paths directly. 58 59 You may have to create /ipfs and /ipns before using 'ipfs mount': 60 61 > sudo mkdir /ipfs /ipns 62 > sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns 63 > ipfs daemon & 64 > ipfs mount 65 66 EXAMPLE: 67 68 # setup 69 > mkdir foo 70 > echo "baz" > foo/bar 71 > ipfs add -r foo 72 added QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR foo/bar 73 added QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC foo 74 > ipfs ls QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC 75 QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 12 bar 76 > ipfs cat QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 77 baz 78 79 # mount 80 > ipfs daemon & 81 > ipfs mount 82 IPFS mounted at: /ipfs 83 IPNS mounted at: /ipns 84 > cd /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC 85 > ls 86 bar 87 > cat bar 88 baz 89 > cat /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC/bar 90 baz 91 > cat /ipfs/QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 92 baz 93 `, 94 }, 95 Options: []cmds.Option{ 96 cmds.StringOption("ipfs-path", "f", "The path where IPFS should be mounted"), 97 cmds.StringOption("ipns-path", "n", "The path where IPNS should be mounted"), 98 }, 99 Run: func(req cmds.Request, res cmds.Response) { 100 cfg, err := req.InvocContext().GetConfig() 101 if err != nil { 102 res.SetError(err, cmds.ErrNormal) 103 return 104 } 105 106 node, err := req.InvocContext().GetNode() 107 if err != nil { 108 res.SetError(err, cmds.ErrNormal) 109 return 110 } 111 112 // error if we aren't running node in online mode 113 if !node.OnlineMode() { 114 res.SetError(errNotOnline, cmds.ErrClient) 115 return 116 } 117 118 fsdir, found, err := req.Option("f").String() 119 if err != nil { 120 res.SetError(err, cmds.ErrNormal) 121 return 122 } 123 if !found { 124 fsdir = cfg.Mounts.IPFS // use default value 125 } 126 127 // get default mount points 128 nsdir, found, err := req.Option("n").String() 129 if err != nil { 130 res.SetError(err, cmds.ErrNormal) 131 return 132 } 133 if !found { 134 nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare! 135 } 136 137 err = Mount(node, fsdir, nsdir) 138 if err != nil { 139 res.SetError(err, cmds.ErrNormal) 140 return 141 } 142 143 var output config.Mounts 144 output.IPFS = fsdir 145 output.IPNS = nsdir 146 res.SetOutput(&output) 147 }, 148 Type: config.Mounts{}, 149 Marshalers: cmds.MarshalerMap{ 150 cmds.Text: func(res cmds.Response) (io.Reader, error) { 151 v := res.Output().(*config.Mounts) 152 s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS) 153 s += fmt.Sprintf("IPNS mounted at: %s\n", v.IPNS) 154 return strings.NewReader(s), nil 155 }, 156 }, 157 } 158 159 func Mount(node *core.IpfsNode, fsdir, nsdir string) error { 160 // check if we already have live mounts. 161 // if the user said "Mount", then there must be something wrong. 162 // so, close them and try again. 163 if node.Mounts.Ipfs != nil { 164 node.Mounts.Ipfs.Unmount() 165 } 166 if node.Mounts.Ipns != nil { 167 node.Mounts.Ipns.Unmount() 168 } 169 170 if err := platformFuseChecks(node); err != nil { 171 return err 172 } 173 174 var err error 175 if err = doMount(node, fsdir, nsdir); err != nil { 176 return err 177 } 178 179 return nil 180 } 181 182 func doMount(node *core.IpfsNode, fsdir, nsdir string) error { 183 fmtFuseErr := func(err error, mountpoint string) error { 184 s := err.Error() 185 if strings.Contains(s, fuseNoDirectory) { 186 s = strings.Replace(s, `fusermount: "fusermount:`, "", -1) 187 s = strings.Replace(s, `\n", exit status 1`, "", -1) 188 return cmds.ClientError(s) 189 } 190 if s == fuseExitStatus1 { 191 s = fmt.Sprintf("fuse failed to access mountpoint %s", mountpoint) 192 return cmds.ClientError(s) 193 } 194 return err 195 } 196 197 // this sync stuff is so that both can be mounted simultaneously. 198 var fsmount mount.Mount 199 var nsmount mount.Mount 200 var err1 error 201 var err2 error 202 203 done := make(chan struct{}) 204 205 go func() { 206 fsmount, err1 = rofs.Mount(node, fsdir) 207 done <- struct{}{} 208 }() 209 210 go func() { 211 nsmount, err2 = ipns.Mount(node, nsdir, fsdir) 212 done <- struct{}{} 213 }() 214 215 <-done 216 <-done 217 218 if err1 != nil || err2 != nil { 219 log.Errorf("error mounting: %s %s", err1, err2) 220 if fsmount != nil { 221 fsmount.Unmount() 222 } 223 if nsmount != nil { 224 nsmount.Unmount() 225 } 226 227 if err1 != nil { 228 return fmtFuseErr(err1, fsdir) 229 } 230 return fmtFuseErr(err2, nsdir) 231 } 232 233 // setup node state, so that it can be cancelled 234 node.Mounts.Ipfs = fsmount 235 node.Mounts.Ipns = nsmount 236 return nil 237 }