Skip to content

Commit 1560931

Browse files
authored
Merge pull request #33 from smkniazi/GH-14
Fixed default user/group information for new files create by a user t…
2 parents c284c7c + 78620af commit 1560931

File tree

5 files changed

+197
-99
lines changed

5 files changed

+197
-99
lines changed

Dir.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"bazil.org/fuse"
1515
"bazil.org/fuse/fs"
1616
"golang.org/x/net/context"
17+
"logicalclocks.com/hopsfs-mount/ugcache"
1718
)
1819

1920
// Encapsulates state and operations for directory node on the HDFS file system
@@ -206,7 +207,7 @@ func (dir *DirINode) LookupAttrs(name string, attrs *Attrs) error {
206207
*attrs, err = dir.FileSystem.getDFSConnector().Stat(path.Join(dir.AbsolutePath(), name))
207208
if err != nil {
208209
// It is a warning as each time new file write tries to stat if the file exists
209-
loginfo("Stat failed", Fields{Operation: Stat, Path: path.Join(dir.AbsolutePath(), name), Error: err})
210+
loginfo("stat failed", Fields{Operation: Stat, Path: path.Join(dir.AbsolutePath(), name), Error: err})
210211
return err
211212
}
212213

@@ -223,8 +224,16 @@ func (dir *DirINode) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node
223224

224225
err := dir.FileSystem.getDFSConnector().Mkdir(dir.AbsolutePathForChild(req.Name), req.Mode)
225226
if err != nil {
227+
loginfo("mkdir failed", Fields{Operation: Mkdir, Path: path.Join(dir.AbsolutePath(), req.Name), Error: err})
226228
return nil, err
227229
}
230+
logdebug("mkdir successful", Fields{Operation: Mkdir, Path: path.Join(dir.AbsolutePath(), req.Name)})
231+
232+
err = dir.changeOwnership(dir.AbsolutePathForChild(req.Name), req.Uid, req.Gid)
233+
if err != nil {
234+
return nil, err
235+
}
236+
228237
return dir.NodeFromAttrs(Attrs{Name: req.Name, Mode: req.Mode | os.ModeDir}), nil
229238
}
230239

@@ -242,9 +251,29 @@ func (dir *DirINode) Create(ctx context.Context, req *fuse.CreateRequest, resp *
242251
return nil, nil, err
243252
}
244253
file.AddHandle(handle)
254+
255+
err = dir.changeOwnership(dir.AbsolutePathForChild(req.Name), req.Uid, req.Gid)
256+
if err != nil {
257+
return nil, nil, err
258+
}
259+
245260
return file, handle, nil
246261
}
247262

263+
func (dir *DirINode) changeOwnership(path string, uid uint32, gid uint32) error {
264+
if hadoopUserID != uid { // the file is created by an other user, so change the ownership information
265+
user := ugcache.LookupUserName(uid)
266+
group := ugcache.LookupGroupName(gid)
267+
err := dir.FileSystem.getDFSConnector().Chown(path, user, group)
268+
if err != nil {
269+
dir.FileSystem.getDFSConnector().Remove(dir.AbsolutePathForChild(path))
270+
return err
271+
}
272+
loginfo("Update ownership", Fields{Operation: Create, Path: path, User: user, Group: group})
273+
}
274+
return nil
275+
}
276+
248277
// Responds on FUSE Remove request
249278
func (dir *DirINode) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
250279
dir.lockMutex()

FileSystem.go

-1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,5 @@ func (filesystem *FileSystem) Statfs(ctx context.Context, req *fuse.StatfsReques
148148
func (filesystem *FileSystem) getDFSConnector() HdfsAccessor {
149149
filesystem.hdfsAccessorsIndex = filesystem.hdfsAccessorsIndex + 1
150150
index := filesystem.hdfsAccessorsIndex % len(filesystem.HdfsAccessors)
151-
loginfo(fmt.Sprintf("Client index %d. len %d", index, len(filesystem.HdfsAccessors)), nil)
152151
return filesystem.HdfsAccessors[index]
153152
}

HdfsAccessor.go

+22-97
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,24 @@
33
package main
44

55
import (
6-
"errors"
76
"fmt"
87
"io"
98
"os"
10-
"os/user"
11-
"strconv"
129
"strings"
1310
"sync"
1411
"syscall"
1512
"time"
1613

1714
"bazil.org/fuse"
1815
"github.com/colinmarc/hdfs/v2"
19-
)
20-
21-
const (
22-
UGCacheTime = 3 * time.Second
16+
"logicalclocks.com/hopsfs-mount/ugcache"
2317
)
2418

2519
// Interface for accessing HDFS
2620
// Concurrency: thread safe: handles unlimited number of concurrent requests
21+
var hadoopUserName string = os.Getenv("HADOOP_USER_NAME")
22+
var hadoopUserID uint32 = 0
23+
2724
type HdfsAccessor interface {
2825
OpenRead(path string) (ReadSeekCloser, error) // Opens HDFS file for reading
2926
CreateFile(path string,
@@ -49,18 +46,11 @@ type TLSConfig struct {
4946
}
5047

5148
type hdfsAccessorImpl struct {
52-
Clock Clock // interface to get wall clock time
53-
NameNodeAddresses []string // array of Address:port string for the name nodes
54-
MetadataClient *hdfs.Client // HDFS client used for metadata operations
55-
MetadataClientMutex sync.Mutex // Serializing all metadata operations for simplicity (for now), TODO: allow N concurrent operations
56-
UserNameToUidCache map[string]UGCacheEntry // cache for converting usernames to UIDs
57-
GroupNameToUidCache map[string]UGCacheEntry // cache for converting usernames to UIDs
58-
TLSConfig TLSConfig // enable/disable using tls
59-
}
60-
61-
type UGCacheEntry struct {
62-
ID uint32 // User/Group Id
63-
Expires time.Time // Absolute time when this cache entry expires
49+
Clock Clock // interface to get wall clock time
50+
NameNodeAddresses []string // array of Address:port string for the name nodes
51+
MetadataClient *hdfs.Client // HDFS client used for metadata operations
52+
MetadataClientMutex sync.Mutex // Serializing all metadata operations for simplicity (for now), TODO: allow N concurrent operations
53+
TLSConfig TLSConfig // enable/disable using tls
6454
}
6555

6656
var _ HdfsAccessor = (*hdfsAccessorImpl)(nil) // ensure hdfsAccessorImpl implements HdfsAccessor
@@ -70,11 +60,9 @@ func NewHdfsAccessor(nameNodeAddresses string, clock Clock, tlsConfig TLSConfig)
7060
nns := strings.Split(nameNodeAddresses, ",")
7161

7262
this := &hdfsAccessorImpl{
73-
NameNodeAddresses: nns,
74-
Clock: clock,
75-
UserNameToUidCache: make(map[string]UGCacheEntry),
76-
GroupNameToUidCache: make(map[string]UGCacheEntry),
77-
TLSConfig: tlsConfig,
63+
NameNodeAddresses: nns,
64+
Clock: clock,
65+
TLSConfig: tlsConfig,
7866
}
7967
return this, nil
8068
}
@@ -103,29 +91,29 @@ func (dfs *hdfsAccessorImpl) ConnectToNameNode() (*hdfs.Client, error) {
10391
client, err := dfs.connectToNameNodeImpl()
10492
if err != nil {
10593
// Connection failed
106-
return nil, errors.New(fmt.Sprintf("Fail to connect to name node with error: %s", err.Error()))
94+
return nil, fmt.Errorf("fail to connect to name node with error: %s", err.Error())
10795
}
10896
return client, nil
10997
}
11098

11199
// Performs an attempt to connect to the HDFS name
112100
func (dfs *hdfsAccessorImpl) connectToNameNodeImpl() (*hdfs.Client, error) {
113-
hadoopUser := os.Getenv("HADOOP_USER_NAME")
114-
if hadoopUser == "" {
115-
u, err := user.Current()
101+
if hadoopUserName == "" {
102+
u, err := ugcache.CurrentUserName()
116103
if err != nil {
117-
return nil, fmt.Errorf("Couldn't determine user: %s", err)
104+
return nil, fmt.Errorf("couldn't determine user: %s", err)
118105
}
119-
hadoopUser = u.Username
106+
hadoopUserName = u
120107
}
121-
loginfo(fmt.Sprintf("Connecting as user: %s", hadoopUser), nil)
108+
hadoopUserID = ugcache.LookupUId(hadoopUserName)
109+
loginfo(fmt.Sprintf("Connecting as user: %s, UID: %d", hadoopUserName, hadoopUserID), nil)
122110

123111
// Performing an attempt to connect to the name node
124112
// Colinmar's hdfs implementation has supported the multiple name node connection
125113
hdfsOptions := hdfs.ClientOptions{
126114
Addresses: dfs.NameNodeAddresses,
127115
TLS: dfs.TLSConfig.TLS,
128-
User: hadoopUser,
116+
User: hadoopUserName,
129117
}
130118

131119
if dfs.TLSConfig.TLS {
@@ -278,11 +266,11 @@ func (dfs *hdfsAccessorImpl) AttrsFromFileInfo(fileInfo os.FileInfo) Attrs {
278266
Name: fileInfo.Name(),
279267
Mode: mode,
280268
Size: fi.Length(),
281-
Uid: dfs.LookupUid(fi.Owner()),
269+
Uid: ugcache.LookupUId(fi.Owner()),
282270
Mtime: modificationTime,
283271
Ctime: modificationTime,
284272
Crtime: modificationTime,
285-
Gid: dfs.LookupGid(fi.OwnerGroup())}
273+
Gid: ugcache.LookupGid(fi.OwnerGroup())}
286274
}
287275

288276
func (dfs *hdfsAccessorImpl) AttrsFromFsInfo(fsInfo hdfs.FsInfo) FsInfo {
@@ -296,69 +284,6 @@ func HadoopTimestampToTime(timestamp uint64) time.Time {
296284
return time.Unix(int64(timestamp)/1000, 0)
297285
}
298286

299-
// Performs a cache-assisted lookup of UID by username
300-
func (dfs *hdfsAccessorImpl) LookupUid(userName string) uint32 {
301-
if userName == "" {
302-
return 0
303-
}
304-
// Note: this method is called under MetadataClientMutex, so accessing the cache dirctionary is safe
305-
cacheEntry, ok := dfs.UserNameToUidCache[userName]
306-
if ok && dfs.Clock.Now().Before(cacheEntry.Expires) {
307-
return cacheEntry.ID
308-
}
309-
310-
u, err := user.Lookup(userName)
311-
if u != nil {
312-
var uid64 uint64
313-
if err == nil {
314-
// UID is returned as string, need to parse it
315-
uid64, err = strconv.ParseUint(u.Uid, 10, 32)
316-
}
317-
if err != nil {
318-
uid64 = (1 << 31) - 1
319-
}
320-
dfs.UserNameToUidCache[userName] = UGCacheEntry{
321-
ID: uint32(uid64),
322-
Expires: dfs.Clock.Now().Add(UGCacheTime)}
323-
return uint32(uid64)
324-
325-
} else {
326-
return 0
327-
}
328-
}
329-
330-
// Performs a cache-assisted lookup of GID by grooupname
331-
func (dfs *hdfsAccessorImpl) LookupGid(groupName string) uint32 {
332-
if groupName == "" {
333-
return 0
334-
}
335-
// Note: this method is called under MetadataClientMutex, so accessing the cache dirctionary is safe
336-
cacheEntry, ok := dfs.GroupNameToUidCache[groupName]
337-
if ok && dfs.Clock.Now().Before(cacheEntry.Expires) {
338-
return cacheEntry.ID
339-
}
340-
341-
g, err := user.LookupGroup(groupName)
342-
if g != nil {
343-
var gid64 uint64
344-
if err == nil {
345-
// GID is returned as string, need to parse it
346-
gid64, err = strconv.ParseUint(g.Gid, 10, 32)
347-
}
348-
if err != nil {
349-
gid64 = (1 << 31) - 1
350-
}
351-
dfs.GroupNameToUidCache[groupName] = UGCacheEntry{
352-
ID: uint32(gid64),
353-
Expires: dfs.Clock.Now().Add(UGCacheTime)}
354-
return uint32(gid64)
355-
356-
} else {
357-
logwarn(fmt.Sprintf("Group not found %s", groupName), nil)
358-
return 0
359-
}
360-
}
361-
362287
// Returns true if err==nil or err is expected (benign) error which should be propagated directoy to the caller
363288
func IsSuccessOrNonRetriableError(err error) bool {
364289
if err == nil {

Log.go

+2
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ const (
3636
Flush = "flush"
3737
Close = "close"
3838
Stat = "stat"
39+
Mkdir = "mkdir"
3940
StatFS = "statfs"
4041
UID = "uid"
4142
GID = "gid"
4243
User = "user"
44+
Group = "group"
4345
Holes = "holes"
4446
Seeks = "seeks"
4547
HardSeeks = "hard_seeks"

0 commit comments

Comments
 (0)