staging.inyokaproject.org

Hardlinks finden

Status: Gelöst | Ubuntu-Version: Kein Ubuntu
Antworten |

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@UlfZibis: Wobei / nicht zwingend das richtige ist, sondern der Mountpoint auf dem das Dateisystem eingehängt ist, in dem Du die Dateien hast, und man muss die Suche auch auf das Dateisystem beschränken. Denn inodes sind nur innerhalb eines Dateisystems eindeutig und auch Hardlinks funktionieren deshalb nicht Dateisystemübergreifend.

Wenn Du Doubletten von Dateinen innerhalb eines Verzeichnisbaums auch ausserhalb des Verzeichnisbaums suchen willst, dann musst Du das gesamte Dateisystem durchsuchen. Klar kannst Du dann auch gleich noch mehr Doubletten finden, aber anders findet man die gewünschte Information nicht. Hardlinks sind Verzeichniseinträge auf den inode einer Datei. Und vom inode kommt man nicht in die andere Richtung, man muss also alle Einträge auf einem Dateisystem durchsuchen wenn man wissen möchte welche Einträge auf einen inode verweisen.

UlfZibis

(Themenstarter)

Anmeldungsdatum:
13. Juli 2011

Beiträge: 3351

Marc_BlackJack_Rintsch schrieb:

Und vom inode kommt man nicht in die andere Richtung, man muss also alle Einträge auf einem Dateisystem durchsuchen wenn man wissen möchte welche Einträge auf einen inode verweisen.

Danke für die klare Zusammenfassung. Im Grunde war mir das auch klar, doch hoffte ich, dass es vielleicht doch noch einen einfacheren und darauf optimierten Weg gibt, als über solch komplizierte Konstrukte mit find. Da die Aufgabenstellung doch sicher öfter vorkommt, verwundert es mich sehr, dass es dafür keinen einfachen Standardbefehl bzw. keine einfache Option eines vorhandenen gibt.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@UlfZibis: Du solltest bei den find-Aufrufen unbedingt noch die Option -xdev verwenden, damit die Suche auf dem selben Dateisystem bleibt.

Ich habe mal spasseshalber etwas in CoffeeScript (1.2) geschrieben was auf dem gleichen Dateisystem bleibt, und auch erkennt wenn es Dateien gibt, die von ausserhalb des Verzeichnisbaums unter dem Startpunkt noch verlinkt sind:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env coffee
'use strict'
_ = require 'lodash'
fs = require 'fs'
path = require 'path'


class SameFiles
  
  constructor: (@inode) -> @items = []

  add: (file) -> @items.push file; @

  getCount: -> @items.length
  
  getLinkCount: -> _.last(@items)?.linkCount ? 0
  
  hasConsistentLinkCount: ->
    expected = @getLinkCount()
    @items.every (file) -> file.linkCount == expected
  
  hasMoreLinksThanFound: -> @getCount() < @getLinkCount()


class FileCollector
  
  constructor: (@deviceID) ->
    @inode2files = {}
  
  isPathOnDevice: (path) ->
    stats = fs.lstatSync path
    stats.dev == @deviceID
  
  add: (stats, filename) ->
    inode = stats.ino
    files = @inode2files[inode] ?= new SameFiles inode
    files.add {linkCount: stats.nlink, name: filename}
    @
  
  read: (basePath) ->
    try
      if @isPathOnDevice basePath
        for name in fs.readdirSync basePath
          subPath = path.join basePath, name
          stats = fs.lstatSync subPath
          if stats.isDirectory()
            @read subPath
          else if stats.isFile() or stats.isSymbolicLink()
            @add stats, subPath
    catch error
      if error.errno? and error.message?
        console.error error.message
      else
        throw error
    
  getSameFiles: ->
    files for inode, files of @inode2files \
      when files.getCount() > 1 or files.hasMoreLinksThanFound()


getSameFiles = (basePath) ->
  baseStats =
    try
      fs.lstatSync basePath
    catch error
      if error.errno? and error.message?
        console.error error.message
        null
      else
        throw error
    
  if baseStats isnt null
    fileCollector = new FileCollector baseStats.dev
    fileCollector.read basePath
    fileCollector.getSameFiles()


main = ->
  if process.argv.length < 3
    console.error "Usage: #{process.argv[1]} base_path"
  else
    basePath = process.argv[2]
    for sameFiles in _.sortBy(getSameFiles(basePath), 'inode')
      unless sameFiles.hasConsistentLinkCount()
        warning = ' links where created/deleted while collecting infos'
      else if sameFiles.hasMoreLinksThanFound()
        diff = sameFiles.getLinkCount() - sameFiles.getCount()
        warning = " has #{diff} more link(s) from somewhere else"
      else
        warning = ''
    
      console.log "inode: #{sameFiles.inode}" + warning
      
      for file in _.sortBy(sameFiles.items, 'name')
        console.log "  #{file.linkCount} #{file.name}"
  null


main() if require.main == module

UlfZibis

(Themenstarter)

Anmeldungsdatum:
13. Juli 2011

Beiträge: 3351

Marc_BlackJack_Rintsch schrieb:

@UlfZibis: Du solltest bei den find-Aufrufen unbedingt noch die Option -xdev verwenden, damit die Suche auf dem selben Dateisystem bleibt.

Guter Hinweis!

Ich habe mal spasseshalber etwas in CoffeeScript (1.2) geschrieben was auf dem gleichen Dateisystem bleibt, und auch erkennt wenn es Dateien gibt, die von ausserhalb des Verzeichnisbaums unter dem Startpunkt noch verlinkt sind:

Ui, jetzt warst Du ja richtig fleißig. Wie könnte man das Skript nennen ... findhardlinks?

Antworten |