2013-11-17 5 views
2

Мне бы хотелось func FolderExists(path string) bool, который сообщит, существует ли папка и доступен для записи. Я получил здесь:Как определить, существует ли папка и доступна ли запись?

func FolderExists(path string) bool { 
    info, err := os.Stat(path) 
    return os.IsExist(err) && info.Mode().IsDir() && info.Mode().??? 
} 

Как узнать, доступна ли эта папка? Я не могу просто проверить разрешения файлов (например, 0200 для разрешения на запись пользователя), потому что тогда мне нужно будет проверить владельца файла. Есть ли простой способ сделать это в Go?

Для тех, кто с UNIX фоны, ищет эквивалент очень просто:

if [ -d "$n" && -w "$n" ] ; then ... fi 
+0

такого рода вещи могут быть сложными; наиболее надежное решение также является самым тупым: попробуйте записать в каталог - если это не так, предположите, что он не доступен для записи. – thwd

+2

Чтобы изменить файловую систему, чтобы проверить разрешения, мне кажется, что это недосмотр. – Ana

ответ

5

Если вы пишете для Unix-у ОС, вы можете использовать unix.Access(path string, mode uint32) error с постоянной unix.W_OK как mode.

Как говорит kostix, вам все равно нужно проверить наличие ошибки при доступе к каталогу (при создании или удалении файла, скажем). Каталог можно было удалить с момента проверки, или разрешений можно было бы изменить, или вы могли бы получить полностью несвязанную ошибку.

(Спасибо пользователю fxxn для указывая на то, что ситуация улучшилась, так как оригинальный ответ в 2013 году, который должен был использовать syscall.Access и извлекать постоянную W_OK из unistd.h.)

Вот код, который проверяет наличие и доступ что дает мне ожидаемый результат в Linux:

package main 

import (
    "fmt" 
    "golang.org/x/sys/unix" 
) 

func writable(path string) bool { 
    return unix.Access(path, unix.W_OK) == nil 
} 

func main() { 
    fmt.Println("/etc writable?", writable("/etc")) 
    fmt.Println("/tmp writable?", writable("/tmp")) 
} 
+0

Обратите внимание, что в настоящее время пакет 'syscall' не должен использоваться. Он заменяется на 'x/sys', который также имеет функцию« Access »(см. [Godoc] (https://godoc.org/golang.org/x/sys/unix#Access)) и даже' W_OK' постоянная. – fxnn

+0

Thanks; обновил ответ. – twotwotwo

+0

В качестве побочного элемента это также работает в каталогах, чтобы проверить, можно ли создавать/удалять файлы внутри. – fxnn

0

Мой ответ не будет отвечать на ваш вопрос напрямую, но все же & hellip;

Вкратце: не делайте этого (если, возможно, вы не пишете что-то встроенное). Объяснение: файловые системы полностью параллельны и, следовательно, по своей сути они являются предметом условий гонки. Проще говоря, последовательность

  1. Проверьте файл.
  2. Если да, прочитайте его.

Невозможно работать в реальном мире, так как файл отлично может исчезнуть между (1) и (2) из-за другого процесса, удаляющего его.

Таким образом, единственный реальный способ написать вышеприведенную последовательность - отбросить (1) и быть готовым к решению проблемы с открытием файла в (2).

Возвращаясь к вашей проблеме, единственный разумный способ - просто попытаться создать файл в целевом каталоге и быть готовым обработать ошибку из-за того, что каталог не доступен для записи. Более того, подход Go к обработке ошибок специально предназначен для таких ситуаций (реального мира).

+0

Я согласен, что вам все равно нужно быть готовым к ошибкам при записи (условия гонки, другие виды ошибок), но я думаю, что есть разумные варианты использования для проверки прав доступа в первую очередь. Может быть, это поможет вашей программе немедленно дать полезное сообщение об ошибке, если в противном случае неудачная запись не будет появляться до тех пор, пока не будет выполнена куча работы. (Дизайнеры POSIX, по-видимому, полагали, что проверка доступа, отдельная от wite, использовалась, поскольку они указали на нее syscall.) – twotwotwo

0
package main 

import (
    "os" 
    "fmt" 
    "syscall" 
) 

func main() { 
    path := "/tmp/xyz1" 

    fmt.Println(IsWritable(path)) 
} 

func IsWritable(path string) (isWritable bool, err error) { 
    isWritable = false 
    info, err := os.Stat(path) 
    if err != nil { 
     fmt.Println("Path doesn't exist") 
     return 
    } 

    err = nil 
    if !info.IsDir() { 
     fmt.Println("Path isn't a directory") 
     return 
    } 

    // Check if the user bit is enabled in file permission 
    if info.Mode().Perm() & (1 << (uint(7))) == 0 { 
     fmt.Println("Write permission bit is not set on this file for user") 
     return 
    } 

    euid := os.Geteuid() 
    // euid will be -1 on Windows - https://golang.org/pkg/os/#Geteuid 
    if euid == -1 { 
     isWritable = true 
     fmt.Println("Warning : Unable to get effective User ID, skipping one final check") 
     return 
    } 

    var stat syscall.Stat_t 
    if err = syscall.Stat(path, &stat); err != nil { 
     fmt.Println("Unable to get stat") 
     return 
    } 

    err = nil 
    if uint32(euid) != stat.Uid { 
     isWritable = false 
     fmt.Println("User doesn't have permission to write to this directory") 
     return 
    } 

    isWritable = true 
    return 
} 
Смежные вопросы