github.com/pelicanplatform/pelican@v1.0.5/client/unique_hash_darwin.go (about) 1 //go:build darwin 2 // +build darwin 3 4 /*************************************************************** 5 * 6 * Copyright (C) 2023, Pelican Project, Morgridge Institute for Research 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); you 9 * may not use this file except in compliance with the License. You may 10 * obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 ***************************************************************/ 21 22 package client 23 24 import ( 25 "bytes" 26 "crypto/sha256" 27 "encoding/binary" 28 "fmt" 29 "syscall" 30 "time" 31 32 log "github.com/sirupsen/logrus" 33 ) 34 35 // Given a local filename, create a unique identifier from filesystem 36 // metadata; anytime the file contents change, the unique identifier 37 // should also change. The hash changes once every 24 hours to allow 38 // the shadow origin to do garbage collection. 39 // 40 // The current algorithm to generate the unique identifier is to 41 // take the concatenation of the following byte buffers: 42 // 43 // - basename of the path. 44 // - inode # as a 64-bit integer serialized to a byte buffer, represented in network order. 45 // - ctime # as a double serialized to a buffer. 46 // - Current unix epoch time, as integer, divided by 86400 47 // 48 // and then running it through a SHA256 sum to produce a digest. 49 // 50 // The hex digest of the SHA256 sum is returned along with the file size 51 func unique_hash(filePath string) (string, uint64, error) { 52 log.Debugf("Creating a unique hash for filename %s", filePath) 53 54 var st syscall.Stat_t 55 if err := syscall.Stat(filePath, &st); err != nil { 56 log.Debugln("Error while stat'ing file for metadata:", err) 57 return "", 0, err 58 } 59 60 buf := new(bytes.Buffer) 61 if err := binary.Write(buf, binary.BigEndian, st.Ino); err != nil { 62 log.Debugln("Error while writing inode to buffer:", err) 63 return "", 0, err 64 } 65 if err := binary.Write(buf, binary.BigEndian, st.Ctimespec.Sec); err != nil { 66 log.Debugln("Error while writing ctime to buffer:", err) 67 return "", 0, err 68 } 69 if err := binary.Write(buf, binary.BigEndian, st.Ctimespec.Nsec); err != nil { 70 log.Debugln("Error while writing ctime nanoseconds to buffer:", err) 71 return "", 0, err 72 } 73 if err := binary.Write(buf, binary.BigEndian, time.Now().Unix()/86400); err != nil { 74 log.Debugln("Error while writing current Unix time to buffer:", err) 75 return "", 0, err 76 } 77 78 digest := sha256.New() 79 digest.Write([]byte(filePath)) 80 digest.Write(buf.Bytes()) 81 82 return fmt.Sprintf("%x", digest.Sum(nil)), uint64(st.Size), nil 83 }