// Request returns the specified data segment by reading it from local disk. // Implements the protocol.Model interface. func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, size int) ([]byte, error) { // Verify that the requested file exists in the local model. m.fmut.RLock() r, ok := m.folderFiles[folder] m.fmut.RUnlock() if !ok { l.Warnf("Request from %s for file %s in nonexistent folder %q", deviceID, name, folder) return nil, ErrNoSuchFile } lf := r.Get(protocol.LocalDeviceID, name) if protocol.IsInvalid(lf.Flags) || protocol.IsDeleted(lf.Flags) { if debug { l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d; invalid: %v", m, deviceID, folder, name, offset, size, lf) } return nil, ErrInvalid } if offset > lf.Size() { if debug { l.Debugf("%v REQ(in; nonexistent): %s: %q o=%d s=%d", m, deviceID, name, offset, size) } return nil, ErrNoSuchFile } if debug && deviceID != protocol.LocalDeviceID { l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size) } m.fmut.RLock() fn := filepath.Join(m.folderCfgs[folder].Path, name) m.fmut.RUnlock() fd, err := os.Open(fn) // XXX: Inefficient, should cache fd? if err != nil { return nil, err } defer fd.Close() buf := make([]byte, size) _, err = fd.ReadAt(buf, offset) if err != nil { return nil, err } return buf, nil }
func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo) { for f := range inbox { if protocol.IsDirectory(f.Flags) || protocol.IsDeleted(f.Flags) { outbox <- f continue } blocks, err := HashFile(filepath.Join(dir, f.Name), blockSize) if err != nil { if debug { l.Debugln("hash error:", f.Name, err) } continue } f.Blocks = blocks outbox <- f } }
func (m *Model) ScanFolderSub(folder, sub string) error { if p := filepath.Clean(filepath.Join(folder, sub)); !strings.HasPrefix(p, folder) { return errors.New("invalid subpath") } m.fmut.RLock() fs, ok := m.folderFiles[folder] dir := m.folderCfgs[folder].Path ignores, _ := ignore.Load(filepath.Join(dir, ".stignore")) m.folderIgnores[folder] = ignores w := &scanner.Walker{ Dir: dir, Sub: sub, Ignores: ignores, BlockSize: protocol.BlockSize, TempNamer: defTempNamer, CurrentFiler: cFiler{m, folder}, IgnorePerms: m.folderCfgs[folder].IgnorePerms, } m.fmut.RUnlock() if !ok { return errors.New("no such folder") } m.setState(folder, FolderScanning) fchan, err := w.Walk() if err != nil { return err } batchSize := 100 batch := make([]protocol.FileInfo, 0, 00) for f := range fchan { events.Default.Log(events.LocalIndexUpdated, map[string]interface{}{ "folder": folder, "name": f.Name, "modified": time.Unix(f.Modified, 0), "flags": fmt.Sprintf("0%o", f.Flags), "size": f.Size(), }) if len(batch) == batchSize { fs.Update(protocol.LocalDeviceID, batch) batch = batch[:0] } batch = append(batch, f) } if len(batch) > 0 { fs.Update(protocol.LocalDeviceID, batch) } batch = batch[:0] // TODO: We should limit the Have scanning to start at sub seenPrefix := false fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi protocol.FileIntf) bool { f := fi.(protocol.FileInfoTruncated) if !strings.HasPrefix(f.Name, sub) { // Return true so that we keep iterating, until we get to the part // of the tree we are interested in. Then return false so we stop // iterating when we've passed the end of the subtree. return !seenPrefix } seenPrefix = true if !protocol.IsDeleted(f.Flags) { if f.IsInvalid() { return true } if len(batch) == batchSize { fs.Update(protocol.LocalDeviceID, batch) batch = batch[:0] } if ignores.Match(f.Name) { // File has been ignored. Set invalid bit. nf := protocol.FileInfo{ Name: f.Name, Flags: f.Flags | protocol.FlagInvalid, Modified: f.Modified, Version: f.Version, // The file is still the same, so don't bump version } events.Default.Log(events.LocalIndexUpdated, map[string]interface{}{ "folder": folder, "name": f.Name, "modified": time.Unix(f.Modified, 0), "flags": fmt.Sprintf("0%o", f.Flags), "size": f.Size(), }) batch = append(batch, nf) } else if _, err := os.Stat(filepath.Join(dir, f.Name)); err != nil && os.IsNotExist(err) { // File has been deleted nf := protocol.FileInfo{ Name: f.Name, Flags: f.Flags | protocol.FlagDeleted, Modified: f.Modified, Version: lamport.Default.Tick(f.Version), } events.Default.Log(events.LocalIndexUpdated, map[string]interface{}{ "folder": folder, "name": f.Name, "modified": time.Unix(f.Modified, 0), "flags": fmt.Sprintf("0%o", f.Flags), "size": f.Size(), }) batch = append(batch, nf) } } return true }) if len(batch) > 0 { fs.Update(protocol.LocalDeviceID, batch) } m.setState(folder, FolderIdle) return nil }