path.go (2331B)
1 package pqutil 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "os/user" 9 "path/filepath" 10 "runtime" 11 "syscall" 12 ) 13 14 // Home gets the PostgreSQL configuration dir in the user's home directory: 15 // %APPDATA%/postgresql on Windows, and $HOME/.postgresql/postgresql.crt 16 // everywhere else. 17 // 18 // Returns an empy string if no home directory was found. 19 // 20 // Matches pqGetHomeDirectory() from PostgreSQL. 21 // https://github.com/postgres/postgres/blob/2b117bb/src/interfaces/libpq/fe-connect.c#L8214 22 func Home(subdir bool) string { 23 if runtime.GOOS == "windows" { 24 // pq uses SHGetFolderPath(), which is deprecated but x/sys/windows has 25 // KnownFolderPath(). We don't really want to pull that in though, so 26 // use APPDATA env. This is also what PostgreSQL uses in some other 27 // codepaths (get_home_path() for example). 28 ad := os.Getenv("APPDATA") 29 if ad == "" { 30 return "" 31 } 32 return filepath.Join(ad, "postgresql") 33 } 34 35 home, _ := os.UserHomeDir() 36 if home == "" { 37 u, err := user.Current() 38 if err != nil { 39 return "" 40 } 41 home = u.HomeDir 42 } 43 // libpq reads some files from ~/ and some from ~/.postgresql – on Windows 44 // it always uses %APPDATA%/postgresql. 45 if subdir { 46 home = filepath.Join(home, ".postgresql") 47 } 48 return home 49 } 50 51 // ErrNotExists reports if err is a "path doesn't exist" type error. 52 // 53 // fs.ErrNotExist is not enough, as "/dev/null/somefile" will return ENOTDIR 54 // instead of ENOENT. 55 func ErrNotExists(err error) bool { 56 perr := new(os.PathError) 57 if errors.As(err, &perr) && (perr.Err == syscall.ENOENT || perr.Err == syscall.ENOTDIR) { 58 return true 59 } 60 return false 61 } 62 63 var WarnFD io.Writer = os.Stderr 64 65 // Pgpass gets the filepath to the pgpass file to use, returning "" if a pgpass 66 // file shouldn't be used. 67 func Pgpass(passfile string) string { 68 // Get passfile from the options. 69 if passfile == "" { 70 home := Home(false) 71 if home == "" { 72 return "" 73 } 74 passfile = filepath.Join(home, ".pgpass") 75 } 76 77 // On Win32, the directory is protected, so we don't have to check the file. 78 if runtime.GOOS != "windows" { 79 fi, err := os.Stat(passfile) 80 if err != nil { 81 return "" 82 } 83 if fi.Mode().Perm()&(0x77) != 0 { 84 fmt.Fprintf(WarnFD, 85 "WARNING: password file %q has group or world access; permissions should be u=rw (0600) or less\n", 86 passfile) 87 return "" 88 } 89 } 90 return passfile 91 }