@cryptotaxi247 / kubo / commits / e55a130b4

Add hidden file support to add

License: MIT Signed-off-by: Gaetan Voyer-Perrault <gatesvp@gmail.com>

gatesvp committed Jul 6, 2015 at 02:38 UTC e55a130b4909448f01ae547dd49cbfd754f4b7af
5 files changed +187 -29
commands/files/is_hidden.go new
+19
@@ -0,0 +1,19 @@
1 +// +build !windows
2 +
3 +package files
4 +
5 +import (
6 + "path/filepath"
7 + "strings"
8 +)
9 +
10 +func IsHidden(f File) bool {
11 +
12 + fName := filepath.Base(f.FileName())
13 +
14 + if strings.HasPrefix(fName, ".") && len(fName) > 1 {
15 + return true
16 + }
17 +
18 + return false
19 +}
commands/files/is_hidden_windows.go new
+29
@@ -0,0 +1,29 @@
1 +// +build windows
2 +
3 +package files
4 +
5 +import (
6 + "path/filepath"
7 + "strings"
8 + "syscall"
9 +)
10 +
11 +func IsHidden(f File) bool {
12 +
13 + fName := filepath.Base(f.FileName())
14 +
15 + if strings.HasPrefix(fName, ".") && len(fName) > 1 {
16 + return true
17 + }
18 +
19 + p, e := syscall.UTF16PtrFromString(f.FileName())
20 + if e != nil {
21 + return false
22 + }
23 +
24 + attrs, e := syscall.GetFileAttributes(p)
25 + if e != nil {
26 + return false
27 + }
28 + return attrs&syscall.FILE_ATTRIBUTE_HIDDEN != 0
29 +}
commands/files/serialfile.go
+1 -1
@@ -3,7 +3,7 @@ package files
3 import (
4 "io"
5 "os"
6 - fp "path"
6 + fp "path/filepath"
7 "sort"
8 "syscall"
9 )
core/commands/add.go
+80 -28
@@ -27,9 +27,12 @@ var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded")
27 const progressReaderIncrement = 1024 * 256
28
29 const (
30 + quietOptionName = "quiet"
31 progressOptionName = "progress"
32 trickleOptionName = "trickle"
33 wrapOptionName = "wrap-with-directory"
34 + hiddenOptionName = "hidden"
35 + onlyHashOptionName = "only-hash"
36 )
37
38 type AddedObject struct {
@@ -54,14 +57,15 @@ remains to be implemented.
57 },
58 Options: []cmds.Option{
59 cmds.OptionRecursivePath, // a builtin option that allows recursive paths (-r, --recursive)
57 - cmds.BoolOption("quiet", "q", "Write minimal output"),
60 + cmds.BoolOption(quietOptionName, "q", "Write minimal output"),
61 cmds.BoolOption(progressOptionName, "p", "Stream progress data"),
59 - cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object"),
62 cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation"),
61 - cmds.BoolOption("only-hash", "n", "Only chunk and hash the specified content, don't write to disk"),
63 + cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk"),
64 + cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object"),
65 + cmds.BoolOption(hiddenOptionName, "Include files that are hidden"),
66 },
67 PreRun: func(req cmds.Request) error {
64 - if quiet, _, _ := req.Option("quiet").Bool(); quiet {
68 + if quiet, _, _ := req.Option(quietOptionName).Bool(); quiet {
69 return nil
70 }
71
@@ -93,7 +97,8 @@ remains to be implemented.
97 progress, _, _ := req.Option(progressOptionName).Bool()
98 trickle, _, _ := req.Option(trickleOptionName).Bool()
99 wrap, _, _ := req.Option(wrapOptionName).Bool()
96 - hash, _, _ := req.Option("only-hash").Bool()
100 + hash, _, _ := req.Option(onlyHashOptionName).Bool()
101 + hidden, _, _ := req.Option(hiddenOptionName).Bool()
102
103 if hash {
104 nilnode, err := core.NewNodeBuilder().NilRepo().Build(n.Context())
@@ -120,7 +125,15 @@ remains to be implemented.
125 return
126 }
127
123 - rootnd, err := addFile(n, file, outChan, progress, wrap, trickle)
128 + addParams := adder{
129 + node: n,
130 + out: outChan,
131 + progress: progress,
132 + wrap: wrap,
133 + hidden: hidden,
134 + trickle: trickle,
135 + }
136 + rootnd, err := addParams.addFile(file)
137 if err != nil {
138 res.SetError(err, cmds.ErrNormal)
139 return
@@ -230,6 +243,17 @@ remains to be implemented.
243 Type: AddedObject{},
244 }
245
246 +// Internal structure for holding the switches passed to the `add` call
247 +type adder struct {
248 + node *core.IpfsNode
249 + out chan interface{}
250 + progress bool
251 + wrap bool
252 + hidden bool
253 + trickle bool
254 +}
255 +
256 +// Perform the actual add & pin locally, outputting results to reader
257 func add(n *core.IpfsNode, reader io.Reader, useTrickle bool) (*dag.Node, error) {
258 var node *dag.Node
259 var err error
@@ -256,49 +280,56 @@ func add(n *core.IpfsNode, reader io.Reader, useTrickle bool) (*dag.Node, error)
280 return node, nil
281 }
282
259 -func addFile(n *core.IpfsNode, file files.File, out chan interface{}, progress bool, wrap bool, useTrickle bool) (*dag.Node, error) {
283 +// Add the given file while respecting the params.
284 +func (params *adder) addFile(file files.File) (*dag.Node, error) {
285 + // Check if file is hidden
286 + if fileIsHidden := files.IsHidden(file); fileIsHidden && !params.hidden {
287 + log.Debugf("%s is hidden, skipping", file.FileName())
288 + return nil, &hiddenFileError{file.FileName()}
289 + }
290 +
291 + // Check if "file" is actually a directory
292 if file.IsDirectory() {
261 - return addDir(n, file, out, progress, useTrickle)
293 + return params.addDir(file)
294 }
295
296 // if the progress flag was specified, wrap the file so that we can send
297 // progress updates to the client (over the output channel)
298 var reader io.Reader = file
267 - if progress {
268 - reader = &progressReader{file: file, out: out}
299 + if params.progress {
300 + reader = &progressReader{file: file, out: params.out}
301 }
302
271 - if wrap {
272 - p, dagnode, err := coreunix.AddWrapped(n, reader, path.Base(file.FileName()))
303 + if params.wrap {
304 + p, dagnode, err := coreunix.AddWrapped(params.node, reader, path.Base(file.FileName()))
305 if err != nil {
306 return nil, err
307 }
276 - out <- &AddedObject{
308 + params.out <- &AddedObject{
309 Hash: p,
310 Name: file.FileName(),
311 }
312 return dagnode, nil
313 }
314
283 - dagnode, err := add(n, reader, useTrickle)
315 + dagnode, err := add(params.node, reader, params.trickle)
316 if err != nil {
317 return nil, err
318 }
319
320 log.Infof("adding file: %s", file.FileName())
289 - if err := outputDagnode(out, file.FileName(), dagnode); err != nil {
321 + if err := outputDagnode(params.out, file.FileName(), dagnode); err != nil {
322 return nil, err
323 }
324 return dagnode, nil
325 }
326
295 -func addDir(n *core.IpfsNode, dir files.File, out chan interface{}, progress bool, useTrickle bool) (*dag.Node, error) {
296 - log.Infof("adding directory: %s", dir.FileName())
297 -
327 +func (params *adder) addDir(file files.File) (*dag.Node, error) {
328 tree := &dag.Node{Data: ft.FolderPBData()}
329 + log.Infof("adding directory: %s", file.FileName())
330
331 for {
301 - file, err := dir.NextFile()
332 + file, err := file.NextFile()
333 if err != nil && err != io.EOF {
334 return nil, err
335 }
@@ -306,30 +337,35 @@ func addDir(n *core.IpfsNode, dir files.File, out chan interface{}, progress boo
337 break
338 }
339
309 - node, err := addFile(n, file, out, progress, false, useTrickle)
310 - if err != nil {
340 + node, err := params.addFile(file)
341 + if _, ok := err.(*hiddenFileError); ok {
342 + // hidden file error, set the node to nil for below
343 + node = nil
344 + } else if err != nil {
345 return nil, err
346 }
347
314 - _, name := path.Split(file.FileName())
348 + if node != nil {
349 + _, name := path.Split(file.FileName())
350
316 - err = tree.AddNodeLink(name, node)
317 - if err != nil {
318 - return nil, err
351 + err = tree.AddNodeLink(name, node)
352 + if err != nil {
353 + return nil, err
354 + }
355 }
356 }
357
322 - err := outputDagnode(out, dir.FileName(), tree)
358 + err := outputDagnode(params.out, file.FileName(), tree)
359 if err != nil {
360 return nil, err
361 }
362
327 - k, err := n.DAG.Add(tree)
363 + k, err := params.node.DAG.Add(tree)
364 if err != nil {
365 return nil, err
366 }
367
332 - n.Pinning.GetManual().PinWithMode(k, pin.Indirect)
368 + params.node.Pinning.GetManual().PinWithMode(k, pin.Indirect)
369
370 return tree, nil
371 }
@@ -349,6 +385,22 @@ func outputDagnode(out chan interface{}, name string, dn *dag.Node) error {
385 return nil
386 }
387
388 +type hiddenFileError struct {
389 + fileName string
390 +}
391 +
392 +func (e *hiddenFileError) Error() string {
393 + return fmt.Sprintf("%s is a hidden file", e.fileName)
394 +}
395 +
396 +type ignoreFileError struct {
397 + fileName string
398 +}
399 +
400 +func (e *ignoreFileError) Error() string {
401 + return fmt.Sprintf("%s is an ignored file", e.fileName)
402 +}
403 +
404 type progressReader struct {
405 file files.File
406 out chan interface{}
test/sharness/t0042-add-skip.sh new
+58
@@ -0,0 +1,58 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2014 Christian Couder
4 +# MIT Licensed; see the LICENSE file in this repository.
5 +#
6 +
7 +test_description="Test add and cat commands"
8 +
9 +. lib/test-lib.sh
10 +
11 +test_add_skip() {
12 +
13 + test_expect_success "'ipfs add -r' with hidden file succeeds" '
14 + mkdir -p mountdir/planets/.asteroids &&
15 + echo "Hello Mars" >mountdir/planets/mars.txt &&
16 + echo "Hello Venus" >mountdir/planets/venus.txt &&
17 + echo "Hello Pluto" >mountdir/planets/.pluto.txt &&
18 + echo "Hello Charon" >mountdir/planets/.charon.txt &&
19 + echo "Hello Ceres" >mountdir/planets/.asteroids/ceres.txt &&
20 + echo "Hello Pallas" >mountdir/planets/.asteroids/pallas.txt &&
21 + ipfs add -r mountdir/planets >actual
22 + '
23 +
24 + test_expect_success "'ipfs add -r' did not include . files" '
25 + echo "added QmZy3khu7qf696i5HtkgL2NotsCZ8wzvNZJ1eUdA5n8KaV mountdir/planets/mars.txt
26 +added QmQnv4m3Q5512zgVtpbJ9z85osQrzZzGRn934AGh6iVEXz mountdir/planets/venus.txt
27 +added QmR8nD1Vzk5twWVC6oShTHvv7mMYkVh6dApCByBJyV2oj3 mountdir/planets" >expected
28 + test_cmp expected actual
29 + '
30 +
31 + test_expect_success "'ipfs add -r --hidden' succeeds" '
32 + ipfs add -r --hidden mountdir/planets >actual
33 + '
34 +
35 + test_expect_success "'ipfs add -r --hidden' did include . files" '
36 + echo "added QmcAREBcjgnUpKfyFmUGnfajA1NQS5ydqRp7WfqZ6JF8Dx mountdir/planets/.asteroids/ceres.txt
37 +added QmZ5eaLybJ5GUZBNwy24AA9EEDTDpA4B8qXnuN3cGxu2uF mountdir/planets/.asteroids/pallas.txt
38 +added Qmf6rbs5GF85anDuoxpSAdtuZPM9D2Yt3HngzjUVSQ7kDV mountdir/planets/.asteroids
39 +added QmaowqjedBkUrMUXgzt9c2ZnAJncM9jpJtkFfgdFstGr5a mountdir/planets/.charon.txt
40 +added QmU4zFD5eJtRBsWC63AvpozM9Atiadg9kPVTuTrnCYJiNF mountdir/planets/.pluto.txt
41 +added QmZy3khu7qf696i5HtkgL2NotsCZ8wzvNZJ1eUdA5n8KaV mountdir/planets/mars.txt
42 +added QmQnv4m3Q5512zgVtpbJ9z85osQrzZzGRn934AGh6iVEXz mountdir/planets/venus.txt
43 +added QmetajtFdmzhWYodAsZoVZSiqpeJDAiaw2NwbM3xcWcpDj mountdir/planets" >expected &&
44 + test_cmp expected actual
45 + '
46 +
47 +}
48 +
49 +# should work offline
50 +test_init_ipfs
51 +test_add_skip
52 +
53 +# should work online
54 +test_launch_ipfs_daemon
55 +test_add_skip
56 +test_kill_ipfs_daemon
57 +
58 +test_done