diff options
Diffstat (limited to 'api')
| -rw-r--r-- | api/Makefile.am | 2 | ||||
| -rw-r--r-- | api/examples/Makefile.am | 6 | ||||
| -rw-r--r-- | api/examples/README | 17 | ||||
| -rwxr-xr-x | api/examples/getvolfile.py | 45 | ||||
| -rwxr-xr-x | api/examples/gfapi.py | 400 | ||||
| -rw-r--r-- | api/examples/glfsxmp.c | 1922 | ||||
| -rw-r--r-- | api/src/Makefile.am | 40 | ||||
| -rw-r--r-- | api/src/README.Symbol_Versions | 3 | ||||
| -rw-r--r-- | api/src/gfapi-messages.h | 147 | ||||
| -rw-r--r-- | api/src/gfapi.aliases | 201 | ||||
| -rw-r--r-- | api/src/gfapi.map | 283 | ||||
| -rw-r--r-- | api/src/glfs-fops.c | 7661 | ||||
| -rw-r--r-- | api/src/glfs-handleops.c | 2655 | ||||
| -rw-r--r-- | api/src/glfs-handles.h | 355 | ||||
| -rw-r--r-- | api/src/glfs-internal.h | 758 | ||||
| -rw-r--r-- | api/src/glfs-master.c | 238 | ||||
| -rw-r--r-- | api/src/glfs-mem-types.h | 29 | ||||
| -rw-r--r-- | api/src/glfs-mgmt.c | 1518 | ||||
| -rw-r--r-- | api/src/glfs-resolve.c | 1744 | ||||
| -rw-r--r-- | api/src/glfs.c | 1982 | ||||
| -rw-r--r-- | api/src/glfs.h | 1285 |
21 files changed, 16630 insertions, 4661 deletions
diff --git a/api/Makefile.am b/api/Makefile.am index af437a64d6d..f0ad1ee971c 100644 --- a/api/Makefile.am +++ b/api/Makefile.am @@ -1 +1 @@ -SUBDIRS = src +SUBDIRS = src examples diff --git a/api/examples/Makefile.am b/api/examples/Makefile.am index 6048bb1c8b7..7112c81d6a7 100644 --- a/api/examples/Makefile.am +++ b/api/examples/Makefile.am @@ -1,4 +1,6 @@ -noinst_PROGRAMS = glfsxmp +# The bits needed for glfsxmp +EXTRA_PROGRAMS = glfsxmp glfsxmp_SOURCES = glfsxmp.c glfsxmp_CFLAGS = $(GLFS_CFLAGS) -Wall -glfsxmp_LDADD = $(GLFS_LIBS)
\ No newline at end of file +glfsxmp_LDADD = $(GLFS_LIBS) -lrt + diff --git a/api/examples/README b/api/examples/README index 8f0bab697d3..4d2b521f779 100644 --- a/api/examples/README +++ b/api/examples/README @@ -17,3 +17,20 @@ install glusterfs-api RPM. Make sure your LDFLAGS includes -L/path/to/lib where libgfapi.so is installed and -I/path/to/include/glusterfs where the 'api' directory containing the headers are available. + +glfsxmp.c +========= + +glfsxmp.c is an example application which uses libgfapi + +Compilation Steps For glfsxmp.c +=============================== + +1. $./autogen.sh +2. $./configure + +Note: Before running ./configure , as mentioned above, you need to + take care of #1 or #2 i.e. pkg-config path or LDFLAGS and + -I/<path> with correct values. + +3. $make glfsxmp diff --git a/api/examples/getvolfile.py b/api/examples/getvolfile.py new file mode 100755 index 00000000000..3b2c8ab5a15 --- /dev/null +++ b/api/examples/getvolfile.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 + +from __future__ import print_function +import ctypes +import ctypes.util + +api = ctypes.CDLL("libgfapi.so") +api.glfs_get_volfile.argtypes = [ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_ulong] +api.glfs_get_volfile.restype = ctypes.c_long + + +def get_volfile(host, volume): + # This is set to a large value to exercise the "buffer not big enough" + # path. More realistically, you'd just start with a huge buffer. + BUF_LEN = 0 + fs = api.glfs_new(volume) + # api.glfs_set_logging(fs,"/dev/stderr",7) + api.glfs_set_volfile_server(fs, "tcp", host, 24007) + api.glfs_init(fs) + vbuf = ctypes.create_string_buffer(BUF_LEN) + vlen = api.glfs_get_volfile(fs, vbuf, BUF_LEN) + if vlen < 0: + vlen = BUF_LEN - vlen + vbuf = ctypes.create_string_buffer(vlen) + vlen = api.glfs_get_volfile(fs, vbuf, vlen) + api.glfs_fini(fs) + if vlen <= 0: + return vlen + return vbuf.value[:vlen] + +if __name__ == "__main__": + import sys + + try: + res = get_volfile(*sys.argv[1:3]) + except: + print("fetching volfile failed (volume not started?)") + + try: + for line in res.split('\n'): + print(line) + except: + print("bad return value %s" % res) diff --git a/api/examples/gfapi.py b/api/examples/gfapi.py deleted file mode 100755 index 7d7a5bab12d..00000000000 --- a/api/examples/gfapi.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/python - -from ctypes import * -import os -import sys -import time -import types - -# Looks like ctypes is having trouble with dependencies, so just force them to -# load with RTLD_GLOBAL until I figure that out. -glfs = CDLL("libglusterfs.so",RTLD_GLOBAL) -xdr = CDLL("libgfxdr.so",RTLD_GLOBAL) -api = CDLL("libgfapi.so",RTLD_GLOBAL) - -# Wow, the Linux kernel folks really play nasty games with this structure. If -# you look at the man page for stat(2) and then at this definition you'll note -# two discrepancies. First, we seem to have st_nlink and st_mode reversed. In -# fact that's exactly how they're defined *for 64-bit systems*; for 32-bit -# they're in the man-page order. Even uglier, the man page makes no mention of -# the *nsec fields, but they are very much present and if they're not included -# then we get memory corruption because libgfapi has a structure definition -# that's longer than ours and they overwrite some random bit of memory after -# the space we allocated. Yes, that's all very disgusting, and I'm still not -# sure this will really work on 32-bit because all of the field types are so -# obfuscated behind macros and feature checks. -class Stat (Structure): - _fields_ = [ - ("st_dev", c_ulong), - ("st_ino", c_ulong), - ("st_nlink", c_ulong), - ("st_mode", c_uint), - ("st_uid", c_uint), - ("st_gid", c_uint), - ("st_rdev", c_ulong), - ("st_size", c_ulong), - ("st_blksize", c_ulong), - ("st_blocks", c_ulong), - ("st_atime", c_ulong), - ("st_atimensec", c_ulong), - ("st_mtime", c_ulong), - ("st_mtimensec", c_ulong), - ("st_ctime", c_ulong), - ("st_ctimensec", c_ulong), - ] -api.glfs_creat.restype = c_void_p -api.glfs_open.restype = c_void_p -api.glfs_lstat.restype = c_int -api.glfs_lstat.argtypes = [c_void_p, c_char_p, POINTER(Stat)] - -class Dirent (Structure): - _fields_ = [ - ("d_ino", c_ulong), - ("d_off", c_ulong), - ("d_reclen", c_ushort), - ("d_type", c_char), - ("d_name", c_char * 256), - ] -api.glfs_opendir.restype = c_void_p -api.glfs_readdir_r.restype = c_int -api.glfs_readdir_r.argtypes = [c_void_p, POINTER(Dirent), - POINTER(POINTER(Dirent))] - -# There's a bit of ctypes glitchiness around __del__ functions and module-level -# variables. If we unload the module while we still have references to File or -# Volume objects, the module-level variables might have disappeared by the time -# __del__ gets called. Therefore the objects hold references which they -# release when __del__ is done. We only actually use the object-local values -# in __del__; for clarity, we just use the simpler module-level form elsewhere. - -class File(object): - - def __init__ (self, fd): - # Add a reference so the module-level variable "api" doesn't - # get yanked out from under us (see comment above File def'n). - self._api = api - self.fd = fd - - def __del__ (self): - self._api.glfs_close(self.fd) - self._api = None - - # File operations, in alphabetical order. - - def fsync (self): - return api.glfs_fsync(self.fd) - - def read (self, buflen, flags=0): - rbuf = create_string_buffer(buflen) - rc = api.glfs_read(self.fd,rbuf,buflen,flags) - if rc > 0: - return rbuf.value[:rc] - else: - return rc - - def read_buffer (self, buf, flags=0): - return api.glfs_read(self.fd,buf,len(buf),flags) - - def write (self, data, flags=0): - return api.glfs_write(self.fd,data,len(data),flags) - -class Dir(object): - - def __init__ (self, fd): - # Add a reference so the module-level variable "api" doesn't - # get yanked out from under us (see comment above File def'n). - self._api = api - self.fd = fd - self.cursor = POINTER(Dirent)() - - def __del__ (self): - self._api.glfs_closedir(self.fd) - self._api = None - - def next (self): - entry = Dirent() - entry.d_reclen = 256 - rc = api.glfs_readdir_r(self.fd,byref(entry),byref(self.cursor)) - if (rc < 0) or (not self.cursor) or (not self.cursor.contents): - return rc - return entry - -class Volume(object): - - # Housekeeping functions. - - def __init__ (self, host, volid, proto="tcp", port=24007): - # Add a reference so the module-level variable "api" doesn't - # get yanked out from under us (see comment above File def'n). - self._api = api - self.fs = api.glfs_new(volid) - api.glfs_set_volfile_server(self.fs,proto,host,port) - - def __del__ (self): - self._api.glfs_fini(self.fs) - self._api = None - - def set_logging (self, path, level): - api.glfs_set_logging(self.fs,path,level) - - def mount (self): - api.glfs_init(self.fs) - - # File operations, in alphabetical order. - - def creat (self, path, flags, mode): - fd = api.glfs_creat(self.fs,path,flags,mode) - if not fd: - return fd - return File(fd) - - def getxattr (self, path, key, maxlen): - buf = create_string_buffer(maxlen) - rc = api.glfs_getxattr(self.fs,path,key,buf,maxlen) - if rc < 0: - return rc - return buf.value[:rc] - - def listxattr (self, path): - buf = create_string_buffer(512) - rc = api.glfs_listxattr(self.fs,path,buf,512) - if rc < 0: - return rc - xattrs = [] - # Parsing character by character is ugly, but it seems like the - # easiest way to deal with the "strings separated by NUL in one - # buffer" format. - i = 0 - while i < rc: - new_xa = buf.raw[i] - i += 1 - while i < rc: - next_char = buf.raw[i] - i += 1 - if next_char == '\0': - xattrs.append(new_xa) - break - new_xa += next_char - xattrs.sort() - return xattrs - - def lstat (self, path): - x = Stat() - rc = api.glfs_lstat(self.fs,path,byref(x)) - if rc >= 0: - return x - else: - return rc - - def mkdir (self, path): - return api.glfs_mkdir(self.fs,path) - - def open (self, path, flags): - fd = api.glfs_open(self.fs,path,flags) - if not fd: - return fd - return File(fd) - - def opendir (self, path): - fd = api.glfs_opendir(self.fs,path) - if not fd: - return fd - return Dir(fd) - - def rename (self, opath, npath): - return api.glfs_rename(self.fs,opath,npath) - - def rmdir (self, path): - return api.glfs_rmdir(self.fs,path) - - def setxattr (self, path, key, value, vlen): - return api.glfs_setxattr(self.fs,path,key,value,vlen,0) - - def unlink (self, path): - return api.glfs_unlink(self.fs,path) - -if __name__ == "__main__": - def test_create_write (vol, path, data): - mypath = path + ".io" - fd = vol.creat(mypath,os.O_WRONLY|os.O_EXCL,0644) - if not fd: - return False, "creat error" - rc = fd.write(data) - if rc != len(data): - return False, "wrote %d/%d bytes" % (rc, len(data)) - return True, "wrote %d bytes" % rc - - # TBD: this test fails if we do create, open, write, read - def test_open_read (vol, path, data): - mypath = path + ".io" - fd = vol.open(mypath,os.O_RDONLY) - if not fd: - return False, "open error" - dlen = len(data) * 2 - buf = fd.read(dlen) - if type(buf) == types.IntType: - return False, "read error %d" % buf - if len(buf) != len(data): - return False, "read %d/%d bytes" % (len(buf), len(data)) - return True, "read '%s'" % buf - - def test_lstat (vol, path, data): - mypath = path + ".io" - sb = vol.lstat(mypath) - if type(sb) == types.IntType: - return False, "lstat error %d" % sb - if sb.st_size != len(data): - return False, "lstat size is %d, expected %d" % ( - sb.st_size, len(data)) - return True, "lstat got correct size %d" % sb.st_size - - def test_rename (vol, path, data): - opath = path + ".io" - npath = path + ".tmp" - rc = vol.rename(opath,npath) - if rc < 0: - return False, "rename error %d" % rc - ofd = vol.open(opath,os.O_RDWR) - if isinstance(ofd,File): - return False, "old path working after rename" - nfd = vol.open(npath,os.O_RDWR) - if isinstance(nfd,File): - return False, "new path not working after rename" - return True, "rename worked" - - def test_unlink (vol, path, data): - mypath = path + ".tmp" - rc = vol.unlink(mypath) - if rc < 0: - return False, "unlink error %d" % fd - fd = vol.open(mypath,os.O_RDWR) - if isinstance(fd,File): - return False, "path still usable after unlink" - return True, "unlink worked" - - def test_mkdir (vol, path, data): - mypath = path + ".dir" - rc = vol.mkdir(mypath) - if rc < 0: - return False, "mkdir error %d" % rc - return True, "mkdir worked" - - def test_create_in_dir (vol, path, data): - mypath = path + ".dir/probe" - fd = vol.creat(mypath,os.O_RDWR,0644) - if not isinstance(fd,File): - return False, "create (in dir) error" - return True, "create (in dir) worked" - - def test_dir_listing (vol, path, data): - mypath = path + ".dir" - fd = vol.opendir(mypath) - if not isinstance(fd,Dir): - return False, "opendir error %d" % fd - files = [] - while True: - ent = fd.next() - if not isinstance(ent,Dirent): - break - name = ent.d_name[:ent.d_reclen] - files.append(name) - if files != [".", "..", "probe"]: - return False, "wrong directory contents" - return True, "directory listing worked" - - def test_unlink_in_dir (vol, path, data): - mypath = path + ".dir/probe" - rc = vol.unlink(mypath) - if rc < 0: - return False, "unlink (in dir) error %d" % rc - return True, "unlink (in dir) worked" - - def test_rmdir (vol, path, data): - mypath = path + ".dir" - rc = vol.rmdir(mypath) - if rc < 0: - return False, "rmdir error %d" % rc - sb = vol.lstat(mypath) - if not isinstance(sb,Stat): - return False, "dir still there after rmdir" - return True, "rmdir worked" - - def test_setxattr (vol, path, data): - mypath = path + ".xa" - fd = vol.creat(mypath,os.O_RDWR|os.O_EXCL,0644) - if not fd: - return False, "creat (xattr test) error" - key1, key2 = "hello", "goodbye" - if vol.setxattr(mypath,"trusted.key1",key1,len(key1)) < 0: - return False, "setxattr (key1) error" - if vol.setxattr(mypath,"trusted.key2",key2,len(key2)) < 0: - return False, "setxattr (key2) error" - return True, "setxattr worked" - - def test_getxattr (vol, path, data): - mypath = path + ".xa" - buf = vol.getxattr(mypath,"trusted.key1",32) - if type(buf) == types.IntType: - return False, "getxattr error" - if buf != "hello": - return False, "wrong getxattr value %s" % buf - return True, "getxattr worked" - - def test_listxattr (vol, path, data): - mypath = path + ".xa" - xattrs = vol.listxattr(mypath) - if type(xattrs) == types.IntType: - return False, "listxattr error" - if xattrs != ["trusted.key1","trusted.key2"]: - return False, "wrong listxattr value %s" % repr(xattrs) - return True, "listxattr worked" - - test_list = ( - test_create_write, - test_open_read, - test_lstat, - test_rename, - test_unlink, - test_mkdir, - test_create_in_dir, - test_dir_listing, - test_unlink_in_dir, - test_rmdir, - test_setxattr, - test_getxattr, - test_listxattr, - ) - - ok_to_fail = ( - # TBD: this fails opening the new file, even though the file - # did get renamed. Looks like a gfapi bug, not ours. - (test_rename, "new path not working after rename"), - # TBD: similar, call returns error even though it worked - (test_rmdir, "dir still there after rmdir"), - ) - - volid, path = sys.argv[1:3] - data = "fubar" - vol = Volume("localhost",volid) - vol.set_logging("/dev/null",7) - #vol.set_logging("/dev/stderr",7) - vol.mount() - - failures = 0 - expected = 0 - for t in test_list: - rc, msg = t(vol,path,data) - if rc: - print "PASS: %s" % msg - else: - print "FAIL: %s" % msg - failures += 1 - for otf in ok_to_fail: - if (t == otf[0]) and (msg == otf[1]): - print " (skipping known failure)" - expected += 1 - break # from the *inner* for loop - else: - break # from the *outer* for loop - - print "%d failures (%d expected)" % (failures, expected) diff --git a/api/examples/glfsxmp.c b/api/examples/glfsxmp.c index 644793a4b1a..a55616ef739 100644 --- a/api/examples/glfsxmp.c +++ b/api/examples/glfsxmp.c @@ -1,247 +1,1811 @@ #include <stdio.h> +#include <stdlib.h> #include <errno.h> -#include "api/glfs.h" +#include <glusterfs/api/glfs.h> +#include <glusterfs/api/glfs-handles.h> #include <string.h> #include <time.h> +#define TEST_STR_LEN 2048 int -test_dirops (glfs_t *fs) +test_dirops(glfs_t *fs) { - glfs_fd_t *fd = NULL; - char buf[512]; - struct dirent *entry = NULL; + glfs_fd_t *fd = NULL; + char buf[512]; + struct dirent *entry = NULL; + + fd = glfs_opendir(fs, "/"); + if (!fd) { + fprintf(stderr, "/: %s\n", strerror(errno)); + return -1; + } + + fprintf(stderr, "Entries:\n"); + while (glfs_readdir_r(fd, (struct dirent *)buf, &entry), entry) { + fprintf(stderr, "%s: %lu\n", entry->d_name, glfs_telldir(fd)); + } + + glfs_closedir(fd); + return 0; +} - fd = glfs_opendir (fs, "/"); - if (!fd) { - fprintf (stderr, "/: %s\n", strerror (errno)); - return -1; - } +int +test_xattr(glfs_t *fs) +{ + char *filename = "/filename2"; + char *linkfile = "/linkfile"; + glfs_fd_t *fd = NULL; + char buf[512]; + char *ptr; + int ret; + + ret = glfs_setxattr(fs, filename, "user.testkey", "testval", 8, 0); + fprintf(stderr, "setxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + + ret = glfs_setxattr(fs, filename, "user.testkey2", "testval", 8, 0); + fprintf(stderr, "setxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + + ret = glfs_getxattr(fs, filename, "user.testkey", buf, 512); + fprintf(stderr, "getxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_listxattr(fs, filename, buf, 512); + fprintf(stderr, "listxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_symlink(fs, "filename", linkfile); + fprintf(stderr, "symlink(%s %s): %s\n", filename, linkfile, + strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_readlink(fs, linkfile, buf, 512); + fprintf(stderr, "readlink(%s) : %d (%s)\n", filename, ret, strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_lsetxattr(fs, filename, "user.testkey3", "testval", 8, 0); + fprintf(stderr, "lsetxattr(%s) : %d (%s)\n", linkfile, ret, + strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_llistxattr(fs, linkfile, buf, 512); + fprintf(stderr, "llistxattr(%s): %d (%s)\n", filename, ret, + strerror(errno)); + if (ret < 0) + return -1; + + ret = glfs_lgetxattr(fs, filename, "user.testkey3", buf, 512); + fprintf(stderr, "lgetxattr(%s): %d (%s)\n", linkfile, ret, strerror(errno)); + if (ret < 0) + return -1; + + for (ptr = buf; ptr < buf + ret; ptr++) { + printf("key=%s\n", ptr); + ptr += strlen(ptr); + } + + ret = glfs_removexattr(fs, filename, "user.testkey2"); + fprintf(stderr, "removexattr(%s): %d (%s)\n", filename, ret, + strerror(errno)); + + fd = glfs_open(fs, filename, O_RDWR); + fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); + + ret = glfs_fsetxattr(fd, "user.testkey2", "testval", 8, 0); + fprintf(stderr, "fsetxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + + ret = glfs_fgetxattr(fd, "user.testkey2", buf, 512); + fprintf(stderr, "fgetxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); + + ret = glfs_flistxattr(fd, buf, 512); + fprintf(stderr, "flistxattr(%s): %d (%s)\n", filename, ret, + strerror(errno)); + if (ret < 0) + return -1; + + for (ptr = buf; ptr < buf + ret; ptr++) { + printf("key=%s\n", ptr); + ptr += strlen(ptr); + } + + ret = glfs_fremovexattr(fd, "user.testkey2"); + fprintf(stderr, "fremovexattr(%s): %d (%s)\n", filename, ret, + strerror(errno)); + + glfs_close(fd); + + return 0; +} - fprintf (stderr, "Entries:\n"); - while (glfs_readdir_r (fd, (struct dirent *)buf, &entry), entry) { - fprintf (stderr, "%s: %lu\n", entry->d_name, glfs_telldir (fd)); - } +int +test_chdir(glfs_t *fs) +{ + int ret = -1; + char *dir = "/dir"; + char *topdir = "/topdir"; + char *linkdir = "/linkdir"; + char *linkdir2 = "/linkdir2"; + char *subdir = "./subdir"; + char *respath = NULL; + char pathbuf[4096]; + + ret = glfs_mkdir(fs, topdir, 0755); + fprintf(stderr, "mkdir(%s): %s\n", topdir, strerror(errno)); + if (ret) + return -1; + + ret = glfs_mkdir(fs, dir, 0755); + fprintf(stderr, "mkdir(%s): %s\n", dir, strerror(errno)); + if (ret) + return -1; + + respath = glfs_getcwd(fs, pathbuf, 4096); + fprintf(stdout, "getcwd() = %s\n", respath); + + ret = glfs_symlink(fs, "topdir", linkdir); + if (ret) { + fprintf(stderr, "symlink(%s, %s): %s\n", topdir, linkdir, + strerror(errno)); + return -1; + } + + ret = glfs_chdir(fs, linkdir); + if (ret) { + fprintf(stderr, "chdir(%s): %s\n", linkdir, strerror(errno)); + return -1; + } + + respath = glfs_getcwd(fs, pathbuf, 4096); + fprintf(stdout, "getcwd() = %s\n", respath); + + respath = glfs_realpath(fs, subdir, pathbuf); + if (respath) { + fprintf(stderr, "realpath(%s) worked unexpectedly: %s\n", subdir, + respath); + return -1; + } + + ret = glfs_mkdir(fs, subdir, 0755); + if (ret) { + fprintf(stderr, "mkdir(%s): %s\n", subdir, strerror(errno)); + return -1; + } + + respath = glfs_realpath(fs, subdir, pathbuf); + if (!respath) { + fprintf(stderr, "realpath(%s): %s\n", subdir, strerror(errno)); + } else { + fprintf(stdout, "realpath(%s) = %s\n", subdir, respath); + } + + ret = glfs_chdir(fs, subdir); + if (ret) { + fprintf(stderr, "chdir(%s): %s\n", subdir, strerror(errno)); + return -1; + } + + respath = glfs_getcwd(fs, pathbuf, 4096); + fprintf(stdout, "getcwd() = %s\n", respath); + + respath = glfs_realpath(fs, "/linkdir/subdir", pathbuf); + if (!respath) { + fprintf(stderr, "realpath(/linkdir/subdir): %s\n", strerror(errno)); + } else { + fprintf(stdout, "realpath(/linkdir/subdir) = %s\n", respath); + } + + return 0; +} - glfs_closedir (fd); - return 0; +#ifdef DEBUG +static void +peek_stat(struct stat *sb) +{ + printf("Dumping stat information:\n"); + printf("File type: "); + + switch (sb->st_mode & S_IFMT) { + case S_IFBLK: + printf("block device\n"); + break; + case S_IFCHR: + printf("character device\n"); + break; + case S_IFDIR: + printf("directory\n"); + break; + case S_IFIFO: + printf("FIFO/pipe\n"); + break; + case S_IFLNK: + printf("symlink\n"); + break; + case S_IFREG: + printf("regular file\n"); + break; + case S_IFSOCK: + printf("socket\n"); + break; + default: + printf("unknown?\n"); + break; + } + + printf("I-node number: %ld\n", (long)sb->st_ino); + + printf("Mode: %lo (octal)\n", + (unsigned long)sb->st_mode); + + printf("Link count: %ld\n", (long)sb->st_nlink); + printf("Ownership: UID=%ld GID=%ld\n", (long)sb->st_uid, + (long)sb->st_gid); + + printf("Preferred I/O block size: %ld bytes\n", (long)sb->st_blksize); + printf("File size: %lld bytes\n", (long long)sb->st_size); + printf("Blocks allocated: %lld\n", (long long)sb->st_blocks); + + printf("Last status change: %s", ctime(&sb->st_ctime)); + printf("Last file access: %s", ctime(&sb->st_atime)); + printf("Last file modification: %s", ctime(&sb->st_mtime)); + + return; } +static void +peek_handle(unsigned char *glid) +{ + int i; -int -test_xattr (glfs_t *fs) + for (i = 0; i < GFAPI_HANDLE_LENGTH; i++) { + printf(":%02x:", glid[i]); + } + printf("\n"); +} +#else /* DEBUG */ +static void +peek_stat(struct stat *sb) { - char *filename = "/filename2"; - char buf[512]; - char *ptr; - int ret; + return; +} - ret = glfs_setxattr (fs, filename, "user.testkey", "testval", 8, 0); - fprintf (stderr, "setxattr(%s): %d (%s)\n", filename, ret, - strerror (errno)); +static void +peek_handle(unsigned char *id) +{ + return; +} +#endif /* DEBUG */ - ret = glfs_setxattr (fs, filename, "user.testkey2", "testval", 8, 0); - fprintf (stderr, "setxattr(%s): %d (%s)\n", filename, ret, - strerror (errno)); +glfs_t *fs = NULL; +char *full_parent_name = "/testdir", *parent_name = "testdir"; - ret = glfs_listxattr (fs, filename, buf, 512); - fprintf (stderr, "listxattr(%s): %d (%s)\n", filename, ret, - strerror (errno)); - if (ret < 0) - return -1; +void +test_h_unlink(void) +{ + char *my_dir = "unlinkdir"; + char *my_file = "file.txt"; + char *my_subdir = "dir1"; + struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL, + *subdir = NULL, *subleaf = NULL; + struct stat sb; + int ret; + + printf("glfs_h_unlink tests: In Progress\n"); + + /* Prepare tests */ + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); + if (dir == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, parent, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + leaf = glfs_h_creat(fs, dir, my_file, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + subdir = glfs_h_mkdir(fs, dir, my_subdir, 0755, &sb); + if (subdir == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_subdir, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + subleaf = glfs_h_creat(fs, subdir, my_file, O_CREAT, 0644, &sb); + if (subleaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, subdir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink non empty directory */ + ret = glfs_h_unlink(fs, dir, my_subdir); + if ((ret && errno != ENOTEMPTY) || (ret == 0)) { + fprintf(stderr, + "glfs_h_unlink: error unlinking %s: it is non empty: %s\n", + my_subdir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink regular file */ + ret = glfs_h_unlink(fs, subdir, my_file); + if (ret) { + fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", + my_file, subdir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink directory */ + ret = glfs_h_unlink(fs, dir, my_subdir); + if (ret) { + fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", + my_subdir, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink regular file */ + ret = glfs_h_unlink(fs, dir, my_file); + if (ret) { + fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", + my_file, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink non-existent regular file */ + ret = glfs_h_unlink(fs, dir, my_file); + if ((ret && errno != ENOENT) || (ret == 0)) { + fprintf(stderr, + "glfs_h_unlink: error unlinking non-existent %s: invalid errno " + ",%d, %s\n", + my_file, ret, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink non-existent directory */ + ret = glfs_h_unlink(fs, dir, my_subdir); + if ((ret && errno != ENOENT) || (ret == 0)) { + fprintf(stderr, + "glfs_h_unlink: error unlinking non-existent %s: invalid " + "errno ,%d, %s\n", + my_subdir, ret, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + /* unlink directory */ + ret = glfs_h_unlink(fs, parent, my_dir); + if (ret) { + fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", + my_dir, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + + printf("glfs_h_unlink tests: PASSED\n"); + +out: + if (dir) + glfs_h_close(dir); + if (leaf) + glfs_h_close(leaf); + if (subdir) + glfs_h_close(subdir); + if (subleaf) + glfs_h_close(subleaf); + if (parent) + glfs_h_close(parent); + + return; +} - for (ptr = buf; ptr < buf + ret; ptr++) { - printf ("key=%s\n", ptr); - ptr += strlen (ptr); - } +void +test_h_getsetattrs(void) +{ + char *my_dir = "attrdir"; + char *my_file = "attrfile.txt"; + struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL; + struct stat sb, retsb; + int ret, valid; + struct timespec timestamp; + + printf("glfs_h_getattrs and setattrs tests: In Progress\n"); + + /* Prepare tests */ + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); + if (dir == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, parent, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_creat(fs, dir, my_file, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, dir, strerror(errno)); + printf("glfs_h_unlink tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + ret = glfs_h_getattrs(fs, dir, &retsb); + if (ret != 0) { + fprintf(stderr, "glfs_h_getattrs: error %s: from (%p),%s\n", my_dir, + dir, strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + peek_stat(&retsb); + /* TODO: Compare stat information */ + + retsb.st_mode = 00666; + retsb.st_uid = 1000; + retsb.st_gid = 1001; + ret = clock_gettime(CLOCK_REALTIME, ×tamp); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + retsb.st_atim = timestamp; + retsb.st_mtim = timestamp; + valid = GFAPI_SET_ATTR_MODE | GFAPI_SET_ATTR_UID | GFAPI_SET_ATTR_GID | + GFAPI_SET_ATTR_ATIME | GFAPI_SET_ATTR_MTIME; + peek_stat(&retsb); + + ret = glfs_h_setattrs(fs, dir, &retsb, valid); + if (ret != 0) { + fprintf(stderr, "glfs_h_setattrs: error %s: from (%p),%s\n", my_dir, + dir, strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + memset(&retsb, 0, sizeof(struct stat)); + ret = glfs_h_stat(fs, dir, &retsb); + if (ret != 0) { + fprintf(stderr, "glfs_h_stat: error %s: from (%p),%s\n", my_dir, dir, + strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + peek_stat(&retsb); + + printf("glfs_h_getattrs and setattrs tests: PASSED\n"); +out: + if (parent) + glfs_h_close(parent); + if (leaf) + glfs_h_close(leaf); + if (dir) + glfs_h_close(dir); + + return; +} - return 0; +void +test_h_truncate(void) +{ + char *my_dir = "truncatedir"; + char *my_file = "file.txt"; + struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL; + struct stat sb; + glfs_fd_t *fd = NULL; + char buf[32]; + off_t offset = 0; + int ret = 0; + + printf("glfs_h_truncate tests: In Progress\n"); + + /* Prepare tests */ + root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (root == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); + if (parent == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, root, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + fd = glfs_h_open(fs, leaf, O_RDWR); + if (fd == NULL) { + fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", my_file, + strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + + memcpy(buf, "abcdefghijklmnopqrstuvwxyz012345", 32); + ret = glfs_write(fd, buf, 32, 0); + + /* run tests */ + /* truncate lower */ + offset = 30; + ret = glfs_h_truncate(fs, leaf, offset); + if (ret != 0) { + fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + ret = glfs_h_getattrs(fs, leaf, &sb); + if (ret != 0) { + fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, + leaf, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + if (sb.st_size != offset) { + fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + + /* truncate higher */ + offset = 32; + ret = glfs_h_truncate(fs, leaf, offset); + if (ret != 0) { + fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + ret = glfs_h_getattrs(fs, leaf, &sb); + if (ret != 0) { + fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, + leaf, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + if (sb.st_size != offset) { + fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + + /* truncate equal */ + offset = 30; + ret = glfs_h_truncate(fs, leaf, offset); + if (ret != 0) { + fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + ret = glfs_h_getattrs(fs, leaf, &sb); + if (ret != 0) { + fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, + leaf, strerror(errno)); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + if (sb.st_size != offset) { + fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); + printf("glfs_h_truncate tests: FAILED\n"); + goto out; + } + + printf("glfs_h_truncate tests: PASSED\n"); +out: + if (fd) + glfs_close(fd); + if (root) + glfs_h_close(root); + if (parent) + glfs_h_close(parent); + if (leaf) + glfs_h_close(leaf); + + return; } +void +test_h_links(void) +{ + char *my_dir = "linkdir"; + char *my_file = "file.txt"; + char *my_symlnk = "slnk.txt"; + char *my_lnk = "lnk.txt"; + char *linksrc_dir = "dir1"; + char *linktgt_dir = "dir2"; + struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, + *dirsrc = NULL, *dirtgt = NULL, *dleaf = NULL; + struct glfs_object *ln1 = NULL; + struct stat sb; + int ret; + char *buf = NULL; + + printf("glfs_h_link(s) tests: In Progress\n"); + + /* Prepare tests */ + root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (root == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); + if (parent == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, root, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dirsrc = glfs_h_mkdir(fs, parent, linksrc_dir, 0755, &sb); + if (dirsrc == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + linksrc_dir, parent, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dirtgt = glfs_h_mkdir(fs, parent, linktgt_dir, 0755, &sb); + if (dirtgt == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + linktgt_dir, parent, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dleaf = glfs_h_creat(fs, dirsrc, my_file, O_CREAT, 0644, &sb); + if (dleaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, dirsrc, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* run tests */ + /* sym link: /testdir/linkdir/file.txt to ./slnk.txt */ + ln1 = glfs_h_symlink(fs, parent, my_symlnk, "./file.txt", &sb); + if (ln1 == NULL) { + fprintf(stderr, "glfs_h_symlink: error creating %s: from (%p),%s\n", + my_symlnk, parent, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + buf = calloc(1024, sizeof(char)); + if (buf == NULL) { + fprintf(stderr, "Error allocating memory\n"); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + + ret = glfs_h_readlink(fs, ln1, buf, 1024); + if (ret <= 0) { + fprintf(stderr, "glfs_h_readlink: error reading %s: from (%p),%s\n", + my_symlnk, ln1, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + if (!(strncmp(buf, my_symlnk, strlen(my_symlnk)))) { + fprintf(stderr, + "glfs_h_readlink: error mismatch in link name: actual %s: " + "retrieved %s\n", + my_symlnk, buf); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + + /* link: /testdir/linkdir/file.txt to ./lnk.txt */ + ret = glfs_h_link(fs, leaf, parent, my_lnk); + if (ret != 0) { + fprintf(stderr, "glfs_h_link: error creating %s: from (%p),%s\n", + my_lnk, parent, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + /* TODO: Should write content to a file and read from the link */ + + /* link: /testdir/linkdir/dir1/file.txt to ../dir2/slnk.txt */ + ret = glfs_h_link(fs, dleaf, dirtgt, my_lnk); + if (ret != 0) { + fprintf(stderr, "glfs_h_link: error creating %s: from (%p),%s\n", + my_lnk, dirtgt, strerror(errno)); + printf("glfs_h_link(s) tests: FAILED\n"); + goto out; + } + /* TODO: Should write content to a file and read from the link */ + + printf("glfs_h_link(s) tests: PASSED\n"); + +out: + if (root) + glfs_h_close(root); + if (parent) + glfs_h_close(parent); + if (leaf) + glfs_h_close(leaf); + if (dirsrc) + glfs_h_close(dirsrc); + if (dirtgt) + glfs_h_close(dirtgt); + if (dleaf) + glfs_h_close(dleaf); + if (ln1) + glfs_h_close(ln1); + if (buf) + free(buf); + + return; +} + +void +test_h_rename(void) +{ + char *my_dir = "renamedir"; + char *my_file = "file.txt"; + char *src_dir = "dir1"; + char *tgt_dir = "dir2"; + struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, + *dirsrc = NULL, *dirtgt = NULL, *dleaf = NULL; + struct stat sb; + int ret; + + printf("glfs_h_rename tests: In Progress\n"); + + /* Prepare tests */ + root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (root == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); + if (parent == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, root, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, parent, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dirsrc = glfs_h_mkdir(fs, parent, src_dir, 0755, &sb); + if (dirsrc == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + src_dir, parent, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dirtgt = glfs_h_mkdir(fs, parent, tgt_dir, 0755, &sb); + if (dirtgt == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + tgt_dir, parent, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + dleaf = glfs_h_creat(fs, dirsrc, my_file, O_CREAT, 0644, &sb); + if (dleaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, dirsrc, strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* run tests */ + /* Rename file.txt -> file1.txt */ + ret = glfs_h_rename(fs, parent, "file.txt", parent, "file1.txt"); + if (ret != 0) { + fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", + "file.txt", "file1.txt", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename dir1/file.txt -> file.txt */ + ret = glfs_h_rename(fs, dirsrc, "file.txt", parent, "file.txt"); + if (ret != 0) { + fprintf(stderr, "glfs_h_rename: error renaming %s/%s to %s (%s)\n", + src_dir, "file.txt", "file.txt", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename file1.txt -> file.txt (exists) */ + ret = glfs_h_rename(fs, parent, "file1.txt", parent, "file.txt"); + if (ret != 0) { + fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", + "file.txt", "file.txt", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename dir1 -> dir3 */ + ret = glfs_h_rename(fs, parent, "dir1", parent, "dir3"); + if (ret != 0) { + fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "dir1", + "dir3", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename dir2 ->dir3 (exists) */ + ret = glfs_h_rename(fs, parent, "dir2", parent, "dir3"); + if (ret != 0) { + fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "dir2", + "dir3", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename file.txt -> dir3 (fail) */ + ret = glfs_h_rename(fs, parent, "file.txt", parent, "dir3"); + if (ret == 0) { + fprintf(stderr, "glfs_h_rename: NO error renaming %s to %s (%s)\n", + "file.txt", "dir3", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + /* rename dir3 -> file.txt (fail) */ + ret = glfs_h_rename(fs, parent, "dir3", parent, "file.txt"); + if (ret == 0) { + fprintf(stderr, "glfs_h_rename: NO error renaming %s to %s (%s)\n", + "dir3", "file.txt", strerror(errno)); + printf("glfs_h_rename tests: FAILED\n"); + goto out; + } + + printf("glfs_h_rename tests: PASSED\n"); + +out: + if (root) + glfs_h_close(root); + if (parent) + glfs_h_close(parent); + if (leaf) + glfs_h_close(leaf); + if (dirsrc) + glfs_h_close(dirsrc); + if (dirtgt) + glfs_h_close(dirtgt); + if (dleaf) + glfs_h_close(dleaf); + + return; +} + +void +assimilatetime(struct timespec *ts, struct timespec ts_st, + struct timespec ts_ed) +{ + if ((ts_ed.tv_nsec - ts_st.tv_nsec) < 0) { + ts->tv_sec += ts_ed.tv_sec - ts_st.tv_sec - 1; + ts->tv_nsec += 1000000000 + ts_ed.tv_nsec - ts_st.tv_nsec; + } else { + ts->tv_sec += ts_ed.tv_sec - ts_st.tv_sec; + ts->tv_nsec += ts_ed.tv_nsec - ts_st.tv_nsec; + } + + if (ts->tv_nsec > 1000000000) { + ts->tv_nsec = ts->tv_nsec - 1000000000; + ts->tv_sec += 1; + } + + return; +} + +#define MAX_FILES_CREATE 10 +#define MAXPATHNAME 512 +void +test_h_performance(void) +{ + char *my_dir = "perftest", *full_dir_path = "/testdir/perftest"; + char *my_file = "file_", my_file_name[MAXPATHNAME]; + struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL; + struct stat sb; + int ret, i; + struct glfs_fd *fd; + struct timespec c_ts = {0, 0}, c_ts_st, c_ts_ed; + struct timespec o_ts = {0, 0}, o_ts_st, o_ts_ed; + + printf("glfs_h_performance tests: In Progress\n"); + + /* Prepare tests */ + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, NULL, strerror(errno)); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + + dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); + if (dir == NULL) { + fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", + my_dir, parent, strerror(errno)); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* create performance */ + ret = clock_gettime(CLOCK_REALTIME, &o_ts_st); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + for (i = 0; i < MAX_FILES_CREATE; i++) { + sprintf(my_file_name, "%s%d", my_file, i); + + ret = clock_gettime(CLOCK_REALTIME, &c_ts_st); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + leaf = glfs_h_lookupat(fs, dir, my_file_name, &sb, 0); + if (leaf != NULL) { + fprintf(stderr, "glfs_h_lookup: exists %s\n", my_file_name); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + + leaf = glfs_h_creat(fs, dir, my_file_name, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", + my_file, dir, strerror(errno)); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + + ret = clock_gettime(CLOCK_REALTIME, &c_ts_ed); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + assimilatetime(&c_ts, c_ts_st, c_ts_ed); + glfs_h_close(leaf); + leaf = NULL; + } + + ret = clock_gettime(CLOCK_REALTIME, &o_ts_ed); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + assimilatetime(&o_ts, o_ts_st, o_ts_ed); + + printf("Creation performance (handle based):\n\t# empty files:%d\n", + MAX_FILES_CREATE); + printf("\tOverall time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", o_ts.tv_sec, + o_ts.tv_nsec); + printf("\tcreate call time time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", + c_ts.tv_sec, c_ts.tv_nsec); + + /* create using path */ + c_ts.tv_sec = o_ts.tv_sec = 0; + c_ts.tv_nsec = o_ts.tv_nsec = 0; + + sprintf(my_file_name, "%s1", full_dir_path); + ret = glfs_mkdir(fs, my_file_name, 0755); + if (ret != 0) { + fprintf(stderr, "glfs_mkdir: error creating %s: from (%p),%s\n", my_dir, + parent, strerror(errno)); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + ret = clock_gettime(CLOCK_REALTIME, &o_ts_st); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + for (i = 0; i < MAX_FILES_CREATE; i++) { + sprintf(my_file_name, "%s1/%sn%d", full_dir_path, my_file, i); + + ret = clock_gettime(CLOCK_REALTIME, &c_ts_st); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + ret = glfs_stat(fs, my_file_name, &sb); + if (ret == 0) { + fprintf(stderr, "glfs_stat: exists %s\n", my_file_name); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + + fd = glfs_creat(fs, my_file_name, O_CREAT, 0644); + if (fd == NULL) { + fprintf(stderr, "glfs_creat: error creating %s: from (%p),%s\n", + my_file, dir, strerror(errno)); + printf("glfs_h_performance tests: FAILED\n"); + goto out; + } + + ret = clock_gettime(CLOCK_REALTIME, &c_ts_ed); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + assimilatetime(&c_ts, c_ts_st, c_ts_ed); + glfs_close(fd); + } + + ret = clock_gettime(CLOCK_REALTIME, &o_ts_ed); + if (ret != 0) { + fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); + printf("glfs_h_getattrs and setattrs tests: FAILED\n"); + goto out; + } + + assimilatetime(&o_ts, o_ts_st, o_ts_ed); + + printf("Creation performance (path based):\n\t# empty files:%d\n", + MAX_FILES_CREATE); + printf("\tOverall time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", o_ts.tv_sec, + o_ts.tv_nsec); + printf("\tcreate call time time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", + c_ts.tv_sec, c_ts.tv_nsec); +out: + return; +} int -test_chdir (glfs_t *fs) -{ - int ret = -1; - ino_t ino = 0; - struct stat st; - char *topdir = "/topdir"; - char *linkdir = "/linkdir"; - char *subdir = "./subdir"; - char *respath = NULL; - char pathbuf[4096]; - - ret = glfs_mkdir (fs, topdir, 0755); - if (ret) { - fprintf (stderr, "mkdir(%s): %s\n", topdir, strerror (errno)); - return -1; - } - - respath = glfs_getcwd (fs, pathbuf, 4096); - fprintf (stdout, "getcwd() = %s\n", respath); - - ret = glfs_symlink (fs, topdir, linkdir); - if (ret) { - fprintf (stderr, "symlink(%s, %s): %s\n", topdir, linkdir, strerror (errno)); - return -1; - } - - ret = glfs_chdir (fs, linkdir); - if (ret) { - fprintf (stderr, "chdir(%s): %s\n", linkdir, strerror (errno)); - return -1; - } - - respath = glfs_getcwd (fs, pathbuf, 4096); - fprintf (stdout, "getcwd() = %s\n", respath); - - respath = glfs_realpath (fs, subdir, pathbuf); - if (respath) { - fprintf (stderr, "realpath(%s) worked unexpectedly: %s\n", subdir, respath); - return -1; - } - - ret = glfs_mkdir (fs, subdir, 0755); - if (ret) { - fprintf (stderr, "mkdir(%s): %s\n", subdir, strerror (errno)); - return -1; - } - - respath = glfs_realpath (fs, subdir, pathbuf); - if (!respath) { - fprintf (stderr, "realpath(%s): %s\n", subdir, strerror (errno)); - } else { - fprintf (stdout, "realpath(%s) = %s\n", subdir, respath); - } - - ret = glfs_chdir (fs, subdir); - if (ret) { - fprintf (stderr, "chdir(%s): %s\n", subdir, strerror (errno)); - return -1; - } - - respath = glfs_getcwd (fs, pathbuf, 4096); - fprintf (stdout, "getcwd() = %s\n", respath); - - respath = glfs_realpath (fs, "/linkdir/subdir", pathbuf); - if (!respath) { - fprintf (stderr, "realpath(/linkdir/subdir): %s\n", strerror (errno)); - } else { - fprintf (stdout, "realpath(/linkdir/subdir) = %s\n", respath); - } - - return 0; +test_handleops(int argc, char *argv[]) +{ + int ret = 0; + glfs_fd_t *fd = NULL; + struct stat sb = { + 0, + }; + struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, *tmp = NULL; + char readbuf[32], writebuf[32]; + unsigned char leaf_handle[GFAPI_HANDLE_LENGTH]; + + char *full_leaf_name = "/testdir/testfile.txt", *leaf_name = "testfile.txt", + *relative_leaf_name = "testdir/testfile.txt"; + char *leaf_name1 = "testfile1.txt"; + char *full_newparent_name = "/testdir/dir1", *newparent_name = "dir1"; + char *full_newnod_name = "/testdir/nod1", *newnod_name = "nod1"; + + /* Initialize test area */ + ret = glfs_mkdir(fs, full_parent_name, 0755); + if (ret != 0 && errno != EEXIST) { + fprintf(stderr, "%s: (%p) %s\n", full_parent_name, fd, strerror(errno)); + printf("Test initialization failed on volume %s\n", argv[1]); + goto out; + } else if (ret != 0) { + printf("Found test directory %s to be existing\n", full_parent_name); + printf("Cleanup test directory and restart tests\n"); + goto out; + } + + fd = glfs_creat(fs, full_leaf_name, O_CREAT, 0644); + if (fd == NULL) { + fprintf(stderr, "%s: (%p) %s\n", full_leaf_name, fd, strerror(errno)); + printf("Test initialization failed on volume %s\n", argv[1]); + goto out; + } + glfs_close(fd); + + printf("Initialized the test area, within volume %s\n", argv[1]); + + /* Handle based APIs test area */ + + /* glfs_lookupat test */ + printf("glfs_h_lookupat tests: In Progress\n"); + /* start at root of the volume */ + root = glfs_h_lookupat(fs, NULL, "/", &sb, 0); + if (root == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", "/", + NULL, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* lookup a parent within root */ + parent = glfs_h_lookupat(fs, root, parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + parent_name, root, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* lookup a leaf/child within the parent */ + leaf = glfs_h_lookupat(fs, parent, leaf_name, &sb, 0); + if (leaf == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + leaf_name, parent, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* reset */ + glfs_h_close(root); + root = NULL; + glfs_h_close(leaf); + leaf = NULL; + glfs_h_close(parent); + parent = NULL; + + /* check absolute paths */ + root = glfs_h_lookupat(fs, NULL, "/", &sb, 0); + if (root == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", "/", + NULL, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, root, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_lookupat(fs, NULL, full_leaf_name, &sb, 0); + if (leaf == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_leaf_name, parent, strerror(errno)); + printf("glfs_h_lookupat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* reset */ + glfs_h_close(leaf); + leaf = NULL; + + /* check multiple component paths */ + leaf = glfs_h_lookupat(fs, root, relative_leaf_name, &sb, 0); + if (leaf == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + relative_leaf_name, parent, strerror(errno)); + goto out; + } + peek_stat(&sb); + + /* reset */ + glfs_h_close(root); + root = NULL; + glfs_h_close(parent); + parent = NULL; + + /* check symlinks in path */ + + /* TODO: -ve test cases */ + /* parent invalid + * path invalid + * path does not exist after some components + * no parent, but relative path + * parent and full path? -ve? + */ + + printf("glfs_h_lookupat tests: PASSED\n"); + + /* glfs_openat test */ + printf("glfs_h_open tests: In Progress\n"); + fd = glfs_h_open(fs, leaf, O_RDWR); + if (fd == NULL) { + fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", + full_leaf_name, strerror(errno)); + printf("glfs_h_open tests: FAILED\n"); + goto out; + } + + /* test read/write based on fd */ + memcpy(writebuf, "abcdefghijklmnopqrstuvwxyz012345", 32); + ret = glfs_write(fd, writebuf, 32, 0); + + glfs_lseek(fd, 0, SEEK_SET); + + ret = glfs_read(fd, readbuf, 32, 0); + if (memcmp(readbuf, writebuf, 32)) { + printf("Failed to read what I wrote: %s %s\n", readbuf, writebuf); + glfs_close(fd); + printf("glfs_h_open tests: FAILED\n"); + goto out; + } + + glfs_h_close(leaf); + leaf = NULL; + glfs_close(fd); + + printf("glfs_h_open tests: PASSED\n"); + + /* Create tests */ + printf("glfs_h_creat tests: In Progress\n"); + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, root, strerror(errno)); + printf("glfs_h_creat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_creat(fs, parent, leaf_name1, O_CREAT, 0644, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_creat: error on create of %s: from (%p),%s\n", + leaf_name1, parent, strerror(errno)); + printf("glfs_h_creat tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + glfs_h_close(leaf); + leaf = NULL; + + leaf = glfs_h_creat(fs, parent, leaf_name1, O_CREAT | O_EXCL, 0644, &sb); + if (leaf != NULL || errno != EEXIST) { + fprintf(stderr, + "glfs_h_creat: existing file, leaf = (%p), errno = %s\n", leaf, + strerror(errno)); + printf("glfs_h_creat tests: FAILED\n"); + if (leaf != NULL) { + glfs_h_close(leaf); + leaf = NULL; + } + } + + tmp = glfs_h_creat(fs, root, parent_name, O_CREAT, 0644, &sb); + if (tmp != NULL || !(errno == EISDIR || errno == EINVAL)) { + fprintf(stderr, "glfs_h_creat: dir create, tmp = (%p), errno = %s\n", + leaf, strerror(errno)); + printf("glfs_h_creat tests: FAILED\n"); + if (tmp != NULL) { + glfs_h_close(tmp); + tmp = NULL; + } + } + + /* TODO: Other combinations and -ve cases as applicable */ + printf("glfs_h_creat tests: PASSED\n"); + + /* extract handle and create from handle test */ + printf( + "glfs_h_extract_handle and glfs_h_create_from_handle tests: In " + "Progress\n"); + /* TODO: Change the lookup to create below for a GIFD recovery failure, + * that needs to be fixed */ + leaf = glfs_h_lookupat(fs, parent, leaf_name1, &sb, 0); + if (leaf == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + leaf_name1, parent, strerror(errno)); + printf("glfs_h_extract_handle tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + ret = glfs_h_extract_handle(leaf, leaf_handle, GFAPI_HANDLE_LENGTH); + if (ret < 0) { + fprintf(stderr, + "glfs_h_extract_handle: error extracting handle of %s: %s\n", + full_leaf_name, strerror(errno)); + printf("glfs_h_extract_handle tests: FAILED\n"); + goto out; + } + peek_handle(leaf_handle); + + glfs_h_close(leaf); + leaf = NULL; + + leaf = glfs_h_create_from_handle(fs, leaf_handle, GFAPI_HANDLE_LENGTH, &sb); + if (leaf == NULL) { + fprintf( + stderr, + "glfs_h_create_from_handle: error on create of %s: from (%p),%s\n", + leaf_name1, leaf_handle, strerror(errno)); + printf("glfs_h_create_from_handle tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + fd = glfs_h_open(fs, leaf, O_RDWR); + if (fd == NULL) { + fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", + full_leaf_name, strerror(errno)); + printf("glfs_h_create_from_handle tests: FAILED\n"); + goto out; + } + + /* test read/write based on fd */ + memcpy(writebuf, "abcdefghijklmnopqrstuvwxyz012345", 32); + ret = glfs_write(fd, writebuf, 32, 0); + + glfs_lseek(fd, 0, SEEK_SET); + + ret = glfs_read(fd, readbuf, 32, 0); + if (memcmp(readbuf, writebuf, 32)) { + printf("Failed to read what I wrote: %s %s\n", writebuf, writebuf); + printf("glfs_h_create_from_handle tests: FAILED\n"); + glfs_close(fd); + goto out; + } + + glfs_close(fd); + glfs_h_close(leaf); + leaf = NULL; + glfs_h_close(parent); + parent = NULL; + + printf( + "glfs_h_extract_handle and glfs_h_create_from_handle tests: PASSED\n"); + + /* Mkdir tests */ + printf("glfs_h_mkdir tests: In Progress\n"); + + ret = glfs_rmdir(fs, full_newparent_name); + if (ret && errno != ENOENT) { + fprintf(stderr, "glfs_rmdir: Failed for %s: %s\n", full_newparent_name, + strerror(errno)); + printf("glfs_h_mkdir tests: FAILED\n"); + goto out; + } + + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, root, strerror(errno)); + printf("glfs_h_mkdir tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_mkdir(fs, parent, newparent_name, 0755, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_mkdir: error on mkdir of %s: from (%p),%s\n", + newparent_name, parent, strerror(errno)); + printf("glfs_h_mkdir tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + glfs_h_close(leaf); + leaf = NULL; + + leaf = glfs_h_mkdir(fs, parent, newparent_name, 0755, &sb); + if (leaf != NULL || errno != EEXIST) { + fprintf(stderr, + "glfs_h_mkdir: existing directory, leaf = (%p), errno = %s\n", + leaf, strerror(errno)); + printf("glfs_h_mkdir tests: FAILED\n"); + if (leaf != NULL) { + glfs_h_close(leaf); + leaf = NULL; + } + } + + glfs_h_close(parent); + parent = NULL; + + printf("glfs_h_mkdir tests: PASSED\n"); + + /* Mknod tests */ + printf("glfs_h_mknod tests: In Progress\n"); + ret = glfs_unlink(fs, full_newnod_name); + if (ret && errno != ENOENT) { + fprintf(stderr, "glfs_unlink: Failed for %s: %s\n", full_newnod_name, + strerror(errno)); + printf("glfs_h_mknod tests: FAILED\n"); + goto out; + } + + parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); + if (parent == NULL) { + fprintf(stderr, + "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", + full_parent_name, root, strerror(errno)); + printf("glfs_h_mknod tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + leaf = glfs_h_mknod(fs, parent, newnod_name, S_IFIFO, 0, &sb); + if (leaf == NULL) { + fprintf(stderr, "glfs_h_mkdir: error on mkdir of %s: from (%p),%s\n", + newnod_name, parent, strerror(errno)); + printf("glfs_h_mknod tests: FAILED\n"); + goto out; + } + peek_stat(&sb); + + /* TODO: create op on a FIFO node hangs, need to check and fix + tmp = glfs_h_creat (fs, parent, newnod_name, O_CREAT, 0644, &sb); + if (tmp != NULL || errno != EINVAL) { + fprintf (stderr, "glfs_h_creat: node create, tmp = (%p), errno = + %s\n", tmp, strerror (errno)); printf ("glfs_h_creat/mknod tests: + FAILED\n"); if (tmp != NULL) { glfs_h_close(tmp); tmp = NULL; + } + } */ + + glfs_h_close(leaf); + leaf = NULL; + + leaf = glfs_h_mknod(fs, parent, newnod_name, 0644, 0, &sb); + if (leaf != NULL || errno != EEXIST) { + fprintf(stderr, + "glfs_h_mknod: existing node, leaf = (%p), errno = %s\n", leaf, + strerror(errno)); + printf("glfs_h_mknod tests: FAILED\n"); + if (leaf != NULL) { + glfs_h_close(leaf); + leaf = NULL; + } + } + + glfs_h_close(parent); + parent = NULL; + + printf("glfs_h_mknod tests: PASSED\n"); + + /* unlink tests */ + test_h_unlink(); + + /* TODO: opendir tests */ + + /* getattr tests */ + test_h_getsetattrs(); + + /* TODO: setattr tests */ + + /* truncate tests */ + test_h_truncate(); + + /* link tests */ + test_h_links(); + + /* rename tests */ + test_h_rename(); + + /* performance tests */ + test_h_performance(); + + /* END: New APIs test area */ + +out: + /* Cleanup glfs handles */ + if (root) + glfs_h_close(root); + if (parent) + glfs_h_close(parent); + if (leaf) + glfs_h_close(leaf); + + return ret; } +int +test_write_apis(glfs_t *fs) +{ + /* Add more content here */ + /* Some apis we can get are */ + /* + 0. glfs_set_xlator_option() + + Read/Write combinations: + . glfs_{p,}readv/{p,}writev + . glfs_pread/pwrite + + tests/basic/gfapi/gfapi-async-calls-test.c + . glfs_read_async/write_async + . glfs_pread_async/pwrite_async + . glfs_readv_async/writev_async + . glfs_preadv_async/pwritev_async + + . ftruncate/ftruncate_async + . fsync/fsync_async + . fdatasync/fdatasync_async + + */ + + glfs_fd_t *fd = NULL; + char *filename = "/filename2"; + int flags = O_RDWR; + char *buf = "some bytes!"; + char writestr[TEST_STR_LEN]; + struct iovec iov = {&writestr, TEST_STR_LEN}; + int ret, i; + + for (i = 0; i < TEST_STR_LEN; i++) + writestr[i] = 0x11; + + fd = glfs_open(fs, filename, flags); + if (!fd) + fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); + + ret = glfs_writev(fd, &iov, 1, flags); + if (ret < 0) { + fprintf(stderr, "writev(%s): %d (%s)\n", filename, ret, + strerror(errno)); + } + + ret = glfs_pwrite(fd, buf, 10, 4, flags, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "pwrite(%s): %d (%s)\n", filename, ret, + strerror(errno)); + } + + ret = glfs_pwritev(fd, &iov, 1, 4, flags); + if (ret < 0) { + fprintf(stderr, "pwritev(%s): %d (%s)\n", filename, ret, + strerror(errno)); + } + + return 0; +} int -main (int argc, char *argv[]) +test_metadata_ops(glfs_t *fs, glfs_t *fs2) { - glfs_t *fs = NULL; - glfs_t *fs2 = NULL; - int ret = 0; - glfs_fd_t *fd = NULL; - glfs_fd_t *fd2 = NULL; - struct stat sb = {0, }; - char readbuf[32]; - char writebuf[32]; + glfs_fd_t *fd = NULL; + glfs_fd_t *fd2 = NULL; + struct stat sb = { + 0, + }; + struct glfs_stat gsb = { + 0, + }; + struct statvfs sfs; + char readbuf[32]; + char writebuf[32]; + + char *filename = "/filename2"; + int ret; + + ret = glfs_lstat(fs, filename, &sb); + fprintf(stderr, "lstat(%s): (%d) %s\n", filename, ret, strerror(errno)); + + fd = glfs_creat(fs, filename, O_RDWR, 0644); + fprintf(stderr, "creat(%s): (%p) %s\n", filename, fd, strerror(errno)); + + fd2 = glfs_open(fs2, filename, O_RDWR); + fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); - char *filename = "/filename2"; + glfs_lseek(fd2, 0, SEEK_SET); - fs = glfs_new ("fsync"); - if (!fs) { - fprintf (stderr, "glfs_new: returned NULL\n"); - return 1; - } + ret = glfs_read(fd2, readbuf, 32, 0); -// ret = glfs_set_volfile (fs, "/tmp/posix.vol"); + printf("read %d, %s", ret, readbuf); - ret = glfs_set_volfile_server (fs, "tcp", "localhost", 24007); + /* get stat */ + ret = glfs_fstat(fd2, &sb); -// ret = glfs_set_volfile_server (fs, "unix", "/tmp/gluster.sock", 0); + ret = glfs_access(fs, filename, R_OK); - ret = glfs_set_logging (fs, "/dev/stderr", 7); + /* set stat */ + /* TODO: got some errors, need to fix */ + /* ret = glfs_fsetattr(fd2, &gsb); */ - ret = glfs_init (fs); + glfs_close(fd); + glfs_close(fd2); - fprintf (stderr, "glfs_init: returned %d\n", ret); + filename = "/filename3"; + ret = glfs_mknod(fs, filename, S_IFIFO, 0); + fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); - sleep (2); + ret = glfs_lstat(fs, filename, &sb); + fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); - fs2 = glfs_new ("fsync"); - if (!fs2) { - fprintf (stderr, "glfs_new: returned NULL\n"); - return 1; - } + ret = glfs_rename(fs, filename, "/filename4"); + fprintf(stderr, "rename(%s): (%d) %s\n", filename, ret, strerror(errno)); + ret = glfs_unlink(fs, "/filename4"); + fprintf(stderr, "unlink(%s): (%d) %s\n", "/filename4", ret, + strerror(errno)); -// ret = glfs_set_volfile (fs2, "/tmp/posix.vol"); + filename = "/dirname2"; + ret = glfs_mkdir(fs, filename, 0); + fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); + + ret = glfs_lstat(fs, filename, &sb); + fprintf(stderr, "lstat(%s): (%d) %s\n", filename, ret, strerror(errno)); + + ret = glfs_rmdir(fs, filename); + fprintf(stderr, "rmdir(%s): (%d) %s\n", filename, ret, strerror(errno)); +} +int +main(int argc, char *argv[]) +{ + glfs_t *fs2 = NULL; + int ret = 0; + glfs_fd_t *fd = NULL; + glfs_fd_t *fd2 = NULL; + struct stat sb = { + 0, + }; + struct glfs_stat gsb = { + 0, + }; + struct statvfs sfs; + char readbuf[32]; + char writebuf[32]; - ret = glfs_set_volfile_server (fs2, "tcp", "localhost", 24007); + char *filename = "/filename2"; - ret = glfs_set_logging (fs2, "/dev/stderr", 7); + if (argc != 3) { + printf("Expect following args\n\t%s <volname> <hostname>\n", argv[0]); + return -1; + } - ret = glfs_init (fs2); + fs = glfs_new(argv[1]); + if (!fs) { + fprintf(stderr, "glfs_new: returned NULL\n"); + return 1; + } - fprintf (stderr, "glfs_init: returned %d\n", ret); + // ret = glfs_set_volfile (fs, "/tmp/posix.vol"); - ret = glfs_lstat (fs, filename, &sb); - fprintf (stderr, "%s: (%d) %s\n", filename, ret, strerror (errno)); + ret = glfs_set_volfile_server(fs, "tcp", argv[2], 24007); - fd = glfs_creat (fs, filename, O_RDWR, 0644); - fprintf (stderr, "%s: (%p) %s\n", filename, fd, strerror (errno)); + // ret = glfs_set_volfile_server (fs, "unix", "/tmp/gluster.sock", 0); - fd2 = glfs_open (fs2, filename, O_RDWR); - fprintf (stderr, "%s: (%p) %s\n", filename, fd, strerror (errno)); + ret = glfs_set_logging(fs, "/dev/stderr", 7); - sprintf (writebuf, "hi there\n"); - ret = glfs_write (fd, writebuf, 32, 0); + ret = glfs_init(fs); - glfs_lseek (fd2, 0, SEEK_SET); + fprintf(stderr, "glfs_init: returned %d\n", ret); - ret = glfs_read (fd2, readbuf, 32, 0); + if (ret) + goto out; - printf ("read %d, %s", ret, readbuf); + sleep(2); - glfs_close (fd); - glfs_close (fd2); + fs2 = glfs_new(argv[1]); + if (!fs2) { + fprintf(stderr, "glfs_new: returned NULL\n"); + return 1; + } - filename = "/filename3"; - ret = glfs_mknod (fs, filename, S_IFIFO, 0); - fprintf (stderr, "%s: (%d) %s\n", filename, ret, strerror (errno)); + // ret = glfs_set_volfile (fs2, "/tmp/posix.vol"); - ret = glfs_lstat (fs, filename, &sb); - fprintf (stderr, "%s: (%d) %s\n", filename, ret, strerror (errno)); + ret = glfs_set_volfile_server(fs2, "tcp", argv[2], 24007); + ret = glfs_set_logging(fs2, "/dev/stderr", 7); - ret = glfs_rename (fs, filename, "/filename4"); - fprintf (stderr, "rename(%s): (%d) %s\n", filename, ret, - strerror (errno)); + ret = glfs_init(fs2); - ret = glfs_unlink (fs, "/filename4"); - fprintf (stderr, "unlink(%s): (%d) %s\n", "/filename4", ret, - strerror (errno)); + fprintf(stderr, "glfs_init: returned %d\n", ret); - filename = "/dirname2"; - ret = glfs_mkdir (fs, filename, 0); - fprintf (stderr, "%s: (%d) %s\n", filename, ret, strerror (errno)); + test_metadata_ops(fs, fs2); - ret = glfs_lstat (fs, filename, &sb); - fprintf (stderr, "lstat(%s): (%d) %s\n", filename, ret, strerror (errno)); + test_dirops(fs); - ret = glfs_rmdir (fs, filename); - fprintf (stderr, "rmdir(%s): (%d) %s\n", filename, ret, strerror (errno)); + test_xattr(fs); - test_dirops (fs); + test_chdir(fs); - test_xattr (fs); + test_handleops(argc, argv); + // done - test_chdir (fs); + /* Test some extra apis */ + test_write_apis(fs); - // done + glfs_statvfs(fs, "/", &sfs); - glfs_fini (fs); - glfs_fini (fs2); + glfs_fini(fs); + glfs_fini(fs2); - return ret; + ret = 0; +out: + return ret; } diff --git a/api/src/Makefile.am b/api/src/Makefile.am index 8980c021c89..7f9a7d17b35 100644 --- a/api/src/Makefile.am +++ b/api/src/Makefile.am @@ -1,31 +1,43 @@ lib_LTLIBRARIES = libgfapi.la -noinst_HEADERS = glfs-mem-types.h glfs-internal.h -libgfapi_HEADERS = glfs.h +noinst_HEADERS = glfs-mem-types.h glfs-internal.h gfapi-messages.h +libgfapi_HEADERS = glfs.h glfs-handles.h libgfapidir = $(includedir)/glusterfs/api -libgfapi_la_SOURCES = glfs.c glfs-mgmt.c glfs-fops.c glfs-resolve.c +EXTRA_DIST = gfapi.map gfapi.aliases + +libgfapi_la_SOURCES = glfs.c glfs-mgmt.c glfs-fops.c glfs-resolve.c \ + glfs-handleops.c libgfapi_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ - $(top_builddir)/rpc/xdr/src/libgfxdr.la \ - $(GF_LDADD) + $(top_builddir)/rpc/xdr/src/libgfxdr.la -libgfapi_la_CPPFLAGS = $(GF_CPPFLAGS) -D__USE_FILE_OFFSET64 \ - -I$(top_srcdir)/libglusterfs/src \ - -I$(top_srcdir)/rpc/rpc-lib/src \ - -I$(top_srcdir)/rpc/xdr/src +libgfapi_la_LDFLAGS = -version-info $(GFAPI_LT_VERSION) $(GF_LDFLAGS) \ + $(GFAPI_EXTRA_LDFLAGS) $(ACL_LIBS) +libgfapi_la_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/rpc/rpc-lib/src \ + -I$(top_srcdir)/rpc/xdr/src \ + -I$(top_builddir)/rpc/xdr/src \ + -DDATADIR=\"$(localstatedir)\" \ + -D__USE_FILE_OFFSET64 -D__USE_LARGEFILE64 + +AM_CFLAGS = -Wall $(GF_CFLAGS) xlator_LTLIBRARIES = api.la xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/mount +# workaround for broken parallel install support in automake with LTLIBRARIES +# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 +install_xlatorLTLIBRARIES = install-xlatorLTLIBRARIES +$(install_xlatorLTLIBRARIES): install-libLTLIBRARIES api_la_SOURCES = glfs-master.c api_la_DEPENDENCIES = libgfapi.la -api_la_LDFLAGS = -module -avoid-version +api_la_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/rpc/xdr/src \ + -I$(top_builddir)/rpc/xdr/src +api_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) +#api_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) $(GF_LDFLAGS) api_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ $(top_builddir)/rpc/xdr/src/libgfxdr.la \ $(top_builddir)/api/src/libgfapi.la - -AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src - -AM_CFLAGS = -Wall $(GF_CFLAGS) diff --git a/api/src/README.Symbol_Versions b/api/src/README.Symbol_Versions new file mode 100644 index 00000000000..b6ec95f9311 --- /dev/null +++ b/api/src/README.Symbol_Versions @@ -0,0 +1,3 @@ + +See ../../doc/developer-guide/gfapi-symbol-versions.md + diff --git a/api/src/gfapi-messages.h b/api/src/gfapi-messages.h new file mode 100644 index 00000000000..b9223940416 --- /dev/null +++ b/api/src/gfapi-messages.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015-2018 Red Hat, Inc. <http://www.redhat.com> + * This file is part of GlusterFS. + * + * This file is licensed to you under your choice of the GNU Lesser + * General Public License, version 3 or any later version (LGPLv3 or + * later), or the GNU General Public License, version 2 (GPLv2), in all + * cases as published by the Free Software Foundation. + * */ + +#ifndef _GFAPI_MESSAGES_H__ +#define _GFAPI_MESSAGES_H__ + +#include <glusterfs/glfs-message-id.h> + +/* To add new message IDs, append new identifiers at the end of the list. + * + * Never remove a message ID. If it's not used anymore, you can rename it or + * leave it as it is, but not delete it. This is to prevent reutilization of + * IDs by other messages. + * + * The component name must match one of the entries defined in + * glfs-message-id.h. + */ + +GLFS_MSGID(API, API_MSG_MEM_ACCT_INIT_FAILED, API_MSG_MASTER_XLATOR_INIT_FAILED, + API_MSG_GFAPI_XLATOR_INIT_FAILED, API_MSG_VOLFILE_OPEN_FAILED, + API_MSG_VOL_SPEC_FILE_ERROR, API_MSG_GLFS_FSOBJ_NULL, + API_MSG_INVALID_ENTRY, API_MSG_FSMUTEX_LOCK_FAILED, + API_MSG_COND_WAIT_FAILED, API_MSG_FSMUTEX_UNLOCK_FAILED, + API_MSG_INODE_REFRESH_FAILED, API_MSG_GRAPH_CONSTRUCT_FAILED, + API_MSG_API_XLATOR_ERROR, API_MSG_XDR_PAYLOAD_FAILED, + API_MSG_GET_VOLINFO_CBK_FAILED, API_MSG_FETCH_VOLUUID_FAILED, + API_MSG_INSUFF_SIZE, API_MSG_FRAME_CREAT_FAILED, + API_MSG_DICT_SET_FAILED, API_MSG_XDR_DECODE_FAILED, + API_MSG_GET_VOLFILE_FAILED, API_MSG_WRONG_OPVERSION, + API_MSG_DICT_SERIALIZE_FAILED, API_MSG_REMOTE_HOST_CONN_FAILED, + API_MSG_VOLFILE_SERVER_EXHAUST, API_MSG_CREATE_RPC_CLIENT_FAILED, + API_MSG_REG_NOTIFY_FUNC_FAILED, API_MSG_REG_CBK_FUNC_FAILED, + API_MSG_GET_CWD_FAILED, API_MSG_FGETXATTR_FAILED, + API_MSG_LOCKINFO_KEY_MISSING, API_MSG_FSETXATTR_FAILED, + API_MSG_FSYNC_FAILED, API_MSG_FDCREATE_FAILED, + API_MSG_INODE_PATH_FAILED, API_MSG_SYNCOP_OPEN_FAILED, + API_MSG_LOCK_MIGRATE_FAILED, API_MSG_OPENFD_SKIPPED, + API_MSG_FIRST_LOOKUP_GRAPH_FAILED, API_MSG_CWD_GRAPH_REF_FAILED, + API_MSG_SWITCHED_GRAPH, API_MSG_XDR_RESPONSE_DECODE_FAILED, + API_MSG_VOLFILE_INFO, API_MSG_VOLFILE_CONNECTING, API_MSG_NEW_GRAPH, + API_MSG_ALLOC_FAILED, API_MSG_CREATE_HANDLE_FAILED, + API_MSG_INODE_LINK_FAILED, API_MSG_STATEDUMP_FAILED, + API_MSG_XREADDIRP_R_FAILED, API_MSG_LOCK_INSERT_MERGE_FAILED, + API_MSG_SETTING_LOCK_TYPE_FAILED, API_MSG_INODE_FIND_FAILED, + API_MSG_FDCTX_SET_FAILED, API_MSG_UPCALL_SYNCOP_FAILED, + API_MSG_INVALID_ARG, API_MSG_UPCALL_EVENT_NULL_RECEIVED, + API_MSG_FLAGS_HANDLE, API_MSG_FDCREATE_FAILED_ON_GRAPH, + API_MSG_TRANS_RDMA_DEP, API_MSG_TRANS_NOT_SUPPORTED, + API_MSG_FS_NOT_INIT, API_MSG_INVALID_SYSRQ, + API_MSG_DECODE_XDR_FAILED, API_MSG_NULL, API_MSG_CALL_NOT_SUCCESSFUL, + API_MSG_CALL_NOT_VALID, API_MSG_UNABLE_TO_DEL, + API_MSG_REMOTE_HOST_DISCONN, API_MSG_HANDLE_NOT_SET); + +#define API_MSG_ALLOC_FAILED_STR "Upcall allocation failed" +#define API_MSG_LOCK_INSERT_MERGE_FAILED_STR \ + "Lock insertion and splitting/merging failed" +#define API_MSG_SETTING_LOCK_TYPE_FAILED_STR "Setting lock type failed" + +#define API_MSG_INVALID_ARG_STR "Invalid" +#define API_MSG_INVALID_ENTRY_STR "Upcall entry validation failed" +#define API_MSG_INODE_FIND_FAILED_STR "Unable to find inode entry" +#define API_MSG_CREATE_HANDLE_FAILED_STR "handle creation failed" +#define API_MSG_UPCALL_EVENT_NULL_RECEIVED_STR \ + "Upcall_EVENT_NULL received. Skipping it" +#define API_MSG_UPCALL_SYNCOP_FAILED_STR "Synctask for upcall failed" +#define API_MSG_FDCREATE_FAILED_STR "Allocating anonymous fd failed" +#define API_MSG_XREADDIRP_R_FAILED_STR "glfs_x_readdirp_r failed" +#define API_MSG_FDCTX_SET_FAILED_STR "Setting fd ctx failed" +#define API_MSG_FLAGS_HANDLE_STR "arg not set. Flags handled are" +#define API_MSG_INODE_REFRESH_FAILED_STR "inode refresh failed" +#define API_MSG_INODE_LINK_FAILED_STR "inode linking failed" +#define API_MSG_GET_CWD_FAILED_STR "Failed to get cwd" +#define API_MSG_FGETXATTR_FAILED_STR "fgetxattr failed" +#define API_MSG_LOCKINFO_KEY_MISSING_STR "missing lockinfo key" +#define API_MSG_FSYNC_FAILED_STR "fsync() failed" +#define API_MSG_FDCREATE_FAILED_ON_GRAPH_STR "fd_create failed on graph" +#define API_MSG_INODE_PATH_FAILED_STR "inode_path failed" +#define API_MSG_SYNCOP_OPEN_FAILED_STR "syncop_open failed" +#define API_MSG_LOCK_MIGRATE_FAILED_STR "lock migration failed on graph" +#define API_MSG_OPENFD_SKIPPED_STR "skipping openfd in graph" +#define API_MSG_FIRST_LOOKUP_GRAPH_FAILED_STR "first lookup on graph failed" +#define API_MSG_CWD_GRAPH_REF_FAILED_STR "cwd refresh of graph failed" +#define API_MSG_SWITCHED_GRAPH_STR "switched to graph" +#define API_MSG_FSETXATTR_FAILED_STR "fsetxattr failed" +#define API_MSG_MEM_ACCT_INIT_FAILED_STR "Memory accounting init failed" +#define API_MSG_MASTER_XLATOR_INIT_FAILED_STR \ + "master xlator for initialization failed" +#define API_MSG_GFAPI_XLATOR_INIT_FAILED_STR \ + "failed to initialize gfapi translator" +#define API_MSG_VOLFILE_OPEN_FAILED_STR "volume file open failed" +#define API_MSG_VOL_SPEC_FILE_ERROR_STR "Cannot reach volume specification file" +#define API_MSG_TRANS_RDMA_DEP_STR \ + "transport RDMA is deprecated, falling back to tcp" +#define API_MSG_TRANS_NOT_SUPPORTED_STR \ + "transport is not supported, possible values tcp|unix" +#define API_MSG_GLFS_FSOBJ_NULL_STR "fs is NULL" +#define API_MSG_FS_NOT_INIT_STR "fs is not properly initialized" +#define API_MSG_FSMUTEX_LOCK_FAILED_STR \ + "pthread lock on glfs mutex, returned error" +#define API_MSG_FSMUTEX_UNLOCK_FAILED_STR \ + "pthread unlock on glfs mutex, returned error" +#define API_MSG_COND_WAIT_FAILED_STR "cond wait failed" +#define API_MSG_INVALID_SYSRQ_STR "not a valid sysrq" +#define API_MSG_GRAPH_CONSTRUCT_FAILED_STR "failed to construct the graph" +#define API_MSG_API_XLATOR_ERROR_STR \ + "api master xlator cannot be specified in volume file" +#define API_MSG_STATEDUMP_FAILED_STR "statedump failed" +#define API_MSG_DECODE_XDR_FAILED_STR \ + "Failed to decode xdr response for GF_CBK_STATEDUMP" +#define API_MSG_NULL_STR "NULL" +#define API_MSG_XDR_PAYLOAD_FAILED_STR "failed to create XDR payload" +#define API_MSG_CALL_NOT_SUCCESSFUL_STR \ + "GET_VOLUME_INFO RPC call is not successful" +#define API_MSG_XDR_RESPONSE_DECODE_FAILED_STR \ + "Failed to decode xdr response for GET_VOLUME_INFO" +#define API_MSG_CALL_NOT_VALID_STR \ + "Response received for GET_VOLUME_INFO RPC is not valid" +#define API_MSG_GET_VOLINFO_CBK_FAILED_STR \ + "In GET_VOLUME_INFO cbk, received error" +#define API_MSG_FETCH_VOLUUID_FAILED_STR "Unable to fetch volume UUID" +#define API_MSG_INSUFF_SIZE_STR "Insufficient size passed" +#define API_MSG_FRAME_CREAT_FAILED_STR "failed to create the frame" +#define API_MSG_DICT_SET_FAILED_STR "failed to set" +#define API_MSG_XDR_DECODE_FAILED_STR "XDR decoding error" +#define API_MSG_GET_VOLFILE_FAILED_STR "failed to get the volume file" +#define API_MSG_VOLFILE_INFO_STR "No change in volfile, continuing" +#define API_MSG_UNABLE_TO_DEL_STR "unable to delete file" +#define API_MSG_WRONG_OPVERSION_STR \ + "Server is operating at an op-version which is not supported" +#define API_MSG_DICT_SERIALIZE_FAILED_STR "Failed to serialize dictionary" +#define API_MSG_REMOTE_HOST_CONN_FAILED_STR "Failed to connect to remote-host" +#define API_MSG_REMOTE_HOST_DISCONN_STR "disconnected from remote-host" +#define API_MSG_VOLFILE_SERVER_EXHAUST_STR "Exhausted all volfile servers" +#define API_MSG_VOLFILE_CONNECTING_STR "connecting to next volfile server" +#define API_MSG_CREATE_RPC_CLIENT_FAILED_STR "failed to create rpc clnt" +#define API_MSG_REG_NOTIFY_FUNC_FAILED_STR "failed to register notify function" +#define API_MSG_REG_CBK_FUNC_FAILED_STR "failed to register callback function" +#define API_MSG_NEW_GRAPH_STR "New graph coming up" +#define API_MSG_HANDLE_NOT_SET_STR "handle not set. Flags handled for xstat are" +#endif /* !_GFAPI_MESSAGES_H__ */ diff --git a/api/src/gfapi.aliases b/api/src/gfapi.aliases new file mode 100644 index 00000000000..bc639e6b99f --- /dev/null +++ b/api/src/gfapi.aliases @@ -0,0 +1,201 @@ + +_priv_glfs_loc_touchup _glfs_loc_touchup$GFAPI_PRIVATE_3.4.0 +_priv_glfs_active_subvol _glfs_active_subvol$GFAPI_PRIVATE_3.4.0 +_priv_glfs_subvol_done _glfs_subvol_done$GFAPI_PRIVATE_3.4.0 +_priv_glfs_init_done _glfs_init_done$GFAPI_PRIVATE_3.4.0 +_priv_glfs_resolve_at _glfs_resolve_at$GFAPI_PRIVATE_3.4.0 + +_pub_glfs_new _glfs_new$GFAPI_3.4.0 +_pub_glfs_set_volfile _glfs_set_volfile$GFAPI_3.4.0 +_pub_glfs_set_volfile_server _glfs_set_volfile_server$GFAPI_3.4.0 +_pub_glfs_set_logging _glfs_set_logging$GFAPI_3.4.0 +_pub_glfs_init _glfs_init$GFAPI_3.4.0 +_pub_glfs_fini _glfs_fini$GFAPI_3.4.0 +_pub_glfs_open _glfs_open$GFAPI_3.4.0 +_pub_glfs_creat _glfs_creat$GFAPI_3.4.0 +_pub_glfs_close _glfs_close$GFAPI_3.4.0 +_pub_glfs_from_glfd _glfs_from_glfd$GFAPI_3.4.0 +_pub_glfs_set_xlator_option _glfs_set_xlator_option$GFAPI_3.4.0 +_pub_glfs_read _glfs_read$GFAPI_3.4.0 +_pub_glfs_write _glfs_write$GFAPI_3.4.0 +_pub_glfs_readv _glfs_readv$GFAPI_3.4.0 +_pub_glfs_writev _glfs_writev$GFAPI_3.4.0 +_pub_glfs_pread34 _glfs_pread$GFAPI_3.4.0 +_pub_glfs_pwrite34 _glfs_pwrite$GFAPI_3.4.0 +_pub_glfs_pread_async34 _glfs_pread_async$GFAPI_3.4.0 +_pub_glfs_pwrite_async34 _glfs_pwrite_async$GFAPI_3.4.0 +_pub_glfs_preadv _glfs_preadv$GFAPI_3.4.0 +_pub_glfs_pwritev _glfs_pwritev$GFAPI_3.4.0 +_pub_glfs_lseek _glfs_lseek$GFAPI_3.4.0 +_pub_glfs_ftruncate34 _glfs_ftruncate$GFAPI_3.4.0 +_pub_glfs_ftruncate_async34 _glfs_ftruncate_async$GFAPI_3.4.0 +_pub_glfs_lstat _glfs_lstat$GFAPI_3.4.0 +_pub_glfs_stat _glfs_stat$GFAPI_3.4.0 +_pub_glfs_fstat _glfs_fstat$GFAPI_3.4.0 +_pub_glfs_fsync34 _glfs_fsync$GFAPI_3.4.0 +_pub_glfs_fsync_async34 _glfs_fsync_async$GFAPI_3.4.0 +_pub_glfs_fdatasync34 _glfs_fdatasync$GFAPI_3.4.0 +_pub_glfs_fdatasync_async34 _glfs_fdatasync_async$GFAPI_3.4.0 +_pub_glfs_access _glfs_access$GFAPI_3.4.0 +_pub_glfs_symlink _glfs_symlink$GFAPI_3.4.0 +_pub_glfs_readlink _glfs_readlink$GFAPI_3.4.0 +_pub_glfs_mknod _glfs_mknod$GFAPI_3.4.0 +_pub_glfs_mkdir _glfs_mkdir$GFAPI_3.4.0 +_pub_glfs_unlink _glfs_unlink$GFAPI_3.4.0 +_pub_glfs_rmdir _glfs_rmdir$GFAPI_3.4.0 +_pub_glfs_rename _glfs_rename$GFAPI_3.4.0 +_pub_glfs_link _glfs_link$GFAPI_3.4.0 +_pub_glfs_opendir _glfs_opendir$GFAPI_3.4.0 +_pub_glfs_readdir_r _glfs_readdir_r$GFAPI_3.4.0 +_pub_glfs_readdirplus_r _glfs_readdirplus_r$GFAPI_3.4.0 +_pub_glfs_telldir _glfs_telldir$GFAPI_3.4.0 +_pub_glfs_seekdir _glfs_seekdir$GFAPI_3.4.0 +_pub_glfs_closedir _glfs_closedir$GFAPI_3.4.0 +_pub_glfs_statvfs _glfs_statvfs$GFAPI_3.4.0 +_pub_glfs_chmod _glfs_chmod$GFAPI_3.4.0 +_pub_glfs_fchmod _glfs_fchmod$GFAPI_3.4.0 +_pub_glfs_chown _glfs_chown$GFAPI_3.4.0 +_pub_glfs_lchown _glfs_lchown$GFAPI_3.4.0 +_pub_glfs_fchown _glfs_fchown$GFAPI_3.4.0 +_pub_glfs_utimens _glfs_utimens$GFAPI_3.4.0 +_pub_glfs_lutimens _glfs_lutimens$GFAPI_3.4.0 +_pub_glfs_futimens _glfs_futimens$GFAPI_3.4.0 +_pub_glfs_getxattr _glfs_getxattr$GFAPI_3.4.0 +_pub_glfs_lgetxattr _glfs_lgetxattr$GFAPI_3.4.0 +_pub_glfs_fgetxattr _glfs_fgetxattr$GFAPI_3.4.0 +_pub_glfs_listxattr _glfs_listxattr$GFAPI_3.4.0 +_pub_glfs_llistxattr _glfs_llistxattr$GFAPI_3.4.0 +_pub_glfs_flistxattr _glfs_flistxattr$GFAPI_3.4.0 +_pub_glfs_setxattr _glfs_setxattr$GFAPI_3.4.0 +_pub_glfs_lsetxattr _glfs_lsetxattr$GFAPI_3.4.0 +_pub_glfs_fsetxattr _glfs_fsetxattr$GFAPI_3.4.0 +_pub_glfs_removexattr _glfs_removexattr$GFAPI_3.4.0 +_pub_glfs_lremovexattr _glfs_lremovexattr$GFAPI_3.4.0 +_pub_glfs_fremovexattr _glfs_fremovexattr$GFAPI_3.4.0 +_pub_glfs_getcwd _glfs_getcwd$GFAPI_3.4.0 +_pub_glfs_chdir _glfs_chdir$GFAPI_3.4.0 +_pub_glfs_fchdir _glfs_fchdir$GFAPI_3.4.0 +_pub_glfs_realpath34 _glfs_realpath$GFAPI_3.4.0 +_pub_glfs_posix_lock _glfs_posix_lock$GFAPI_3.4.0 +_pub_glfs_dup _glfs_dup$GFAPI_3.4.0 + +_pub_glfs_setfsuid _glfs_setfsuid$GFAPI_3.4.2 +_pub_glfs_setfsgid _glfs_setfsgid$GFAPI_3.4.2 +_pub_glfs_setfsgroups _glfs_setfsgroups$GFAPI_3.4.2 +_pub_glfs_h_lookupat34 _glfs_h_lookupat$GFAPI_3.4.2 +_pub_glfs_h_creat _glfs_h_creat$GFAPI_3.4.2 +_pub_glfs_h_mkdir _glfs_h_mkdir$GFAPI_3.4.2 +_pub_glfs_h_mknod _glfs_h_mknod$GFAPI_3.4.2 +_pub_glfs_h_symlink _glfs_h_symlink$GFAPI_3.4.2 +_pub_glfs_h_unlink _glfs_h_unlink$GFAPI_3.4.2 +_pub_glfs_h_close _glfs_h_close$GFAPI_3.4.2 +_pub_glfs_h_truncate _glfs_h_truncate$GFAPI_3.4.2 +_pub_glfs_h_stat _glfs_h_stat$GFAPI_3.4.2 +_pub_glfs_h_getattrs _glfs_h_getattrs$GFAPI_3.4.2 +_pub_glfs_h_setattrs _glfs_h_setattrs$GFAPI_3.4.2 +_pub_glfs_h_readlink _glfs_h_readlink$GFAPI_3.4.2 +_pub_glfs_h_link _glfs_h_link$GFAPI_3.4.2 +_pub_glfs_h_rename _glfs_h_rename$GFAPI_3.4.2 +_pub_glfs_h_extract_handle _glfs_h_extract_handle$GFAPI_3.4.2 +_pub_glfs_h_create_from_handle _glfs_h_create_from_handle$GFAPI_3.4.2 +_pub_glfs_h_opendir _glfs_h_opendir$GFAPI_3.4.2 +_pub_glfs_h_open _glfs_h_open$GFAPI_3.4.2 + +_pub_glfs_get_volumeid _glfs_get_volumeid$GFAPI_3.5.0 +_pub_glfs_readdir _glfs_readdir$GFAPI_3.5.0 +_pub_glfs_readdirplus _glfs_readdirplus$GFAPI_3.5.0 +_pub_glfs_fallocate _glfs_fallocate$GFAPI_3.5.0 +_pub_glfs_discard _glfs_discard$GFAPI_3.5.0 +_pub_glfs_zerofill _glfs_zerofill$GFAPI_3.5.0 +_pub_glfs_caller_specific_init _glfs_caller_specific_init$GFAPI_3.5.0 +_pub_glfs_h_setxattrs _glfs_h_setxattrs$GFAPI_3.5.0 + +_pub_glfs_unset_volfile_server _glfs_unset_volfile_server$GFAPI_3.5.1 +_pub_glfs_h_getxattrs _glfs_h_getxattrs$GFAPI_3.5.1 +_pub_glfs_h_removexattrs _glfs_h_removexattrs$GFAPI_3.5.1 + +_pub_glfs_get_volfile _glfs_get_volfile$GFAPI_3.6.0 +_pub_glfs_h_access _glfs_h_access$GFAPI_3.6.0 + +_pub_glfs_h_poll_upcall370 _glfs_h_poll_upcall$GFAPI_3.7.0 +_pub_glfs_h_acl_set _glfs_h_acl_set$GFAPI_3.7.0 +_pub_glfs_h_acl_get _glfs_h_acl_get$GFAPI_3.7.0 +_pub_glfs_h_statfs _glfs_h_statfs$GFAPI_3.7.0 +_pub_glfs_h_anonymous_read _glfs_h_anonymous_read$GFAPI_3.7.0 +_pub_glfs_h_anonymous_write _glfs_h_anonymous_write$GFAPI_3.7.0 + +_priv_glfs_free_from_ctx _glfs_free_from_ctx$GFAPI_PRIVATE_3.7.0 +_priv_glfs_new_from_ctx _glfs_new_from_ctx$GFAPI_PRIVATE_3.7.0 +_priv_glfs_resolve _glfs_resolve$GFAPI_PRIVATE_3.7.0 +_priv_glfs_process_upcall_event _glfs_process_upcall_event$GFAPI_PRIVATE_3.7.0 + +_pub_glfs_h_lookupat _glfs_h_lookupat$GFAPI_3.7.4 + +_pub_glfs_truncate _glfs_truncate$GFAPI_3.7.15 + +_pub_glfs_free _glfs_free$GFAPI_3.7.16 +_pub_glfs_h_poll_upcall _glfs_h_poll_upcall$GFAPI_3.7.16 +_pub_glfs_upcall_get_fs _glfs_upcall_get_fs$GFAPI_3.7.16 +_pub_glfs_upcall_get_reason _glfs_upcall_get_reason$GFAPI_3.7.16 +_pub_glfs_upcall_get_event _glfs_upcall_get_event$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_object _glfs_upcall_inode_get_object$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_flags _glfs_upcall_inode_get_flags$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_stat _glfs_upcall_inode_get_stat$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_expire _glfs_upcall_inode_get_expire$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_pobject _glfs_upcall_inode_get_pobject$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_pstat _glfs_upcall_inode_get_pstat$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_oldpobject _glfs_upcall_inode_get_oldpobject$GFAPI_3.7.16 +_pub_glfs_upcall_inode_get_oldpstat _glfs_upcall_inode_get_oldpstat$GFAPI_3.7.16 + +_pub_glfs_realpath _glfs_realpath$GFAPI_3.7.17 + +_pub_glfs_sysrq _glfs_sysrq$GFAPI_3.10.0 + +_pub_glfs_fd_set_lkowner _glfs_fd_set_lkowner$GFAPI_3.10.7 + +_pub_glfs_xreaddirplus_r _glfs_xreaddirplus_r$GFAPI_3.11.0 +_pub_glfs_xreaddirplus_r_get_stat _glfs_xreaddirplus_r_get_stat$GFAPI_3.11.0 +_pub_glfs_xreaddirplus_r_get_object _glfs_xreaddirplus_r_get_object$GFAPI_3.11.0 +_pub_glfs_object_copy _glfs_object_copy$GFAPI_3.11.0 + +_priv_glfs_ipc _glfs_ipc$GFAPI_3.12.0 + +_pub_glfs_upcall_register _glfs_upcall_register$GFAPI_3.13.0 +_pub_glfs_upcall_unregister _glfs_upcall_unregister$GFAPI_3.13.0 + +_pub_glfs_setfsleaseid _glfs_setfsleaseid$GFAPI_4.0.0 +_pub_glfs_file_lock _glfs_file_lock$GFAPI_4.0.0 +_pub_glfs_lease _glfs_lease$GFAPI_4.0.0 +_pub_glfs_h_lease _glfs_h_lease$GFAPI_4.0.0 +_pub_glfs_upcall_lease_get_object _glfs_upcall_lease_get_object$GFAPI_4.1.6 +_pub_glfs_upcall_lease_get_lease_type _glfs_upcall_lease_get_lease_type$GFAPI_4.1.6 + +_priv_glfs_statx _glfs_statx$GFAPI_6.0 +_priv_glfs_iatt_from_statx _glfs_iatt_from_statx$GFAPI_6.0 +_priv_glfs_setfspid _glfs_setfspid$GFAPI_6.1 + +_pub_glfs_read_async _glfs_read_async$GFAPI_6.0 +_pub_glfs_write_async _glfs_write_async$GFAPI_6.0 +_pub_glfs_readv_async _glfs_readv_async$GFAPI_6.0 +_pub_glfs_writev_async _glfs_writev_async$GFAPI_6.0 +_pub_glfs_pread _glfs_pread$GFAPI_6.0 +_pub_glfs_pwrite _glfs_pwrite$GFAPI_6.0 +_pub_glfs_pread_async _glfs_pread_async$GFAPI_6.0 +_pub_glfs_pwrite_async _glfs_pwrite_async$GFAPI_6.0 +_pub_glfs_preadv_async _glfs_preadv_async$GFAPI_6.0 +_pub_glfs_pwritev_async _glfs_pwritev_async$GFAPI_6.0 +_pub_glfs_fsync _glfs_fsync$GFAPI_6.0 +_pub_glfs_fsync_async _glfs_fsync_async$GFAPI_6.0 +_pub_glfs_fdatasync _glfs_fdatasync$GFAPI_6.0 +_pub_glfs_fdatasync_async _glfs_fdatasync_async$GFAPI_6.0 +_pub_glfs_ftruncate _glfs_ftruncate$GFAPI_6.0 +_pub_glfs_ftruncate_async _glfs_ftruncate_async$GFAPI_6.0 +_pub_glfs_discard_async _glfs_discard_async$GFAPI_6.0 +_pub_glfs_zerofill_async _glfs_zerofill_async$GFAPI_6.0 +_pub_glfs_copy_file_range _glfs_copy_file_range$GFAPI_6.0 +_pub_glfs_fsetattr _glfs_fsetattr$GFAPI_6.0 +_pub_glfs_setattr _glfs_setattr$GFAPI_6.0 + +_pub_glfs_set_statedump_path _glfs_set_statedump_path@GFAPI_7.0 + +_pub_glfs_h_creat_open _glfs_h_creat_open@GFAPI_6.6 diff --git a/api/src/gfapi.map b/api/src/gfapi.map new file mode 100644 index 00000000000..228ac47c084 --- /dev/null +++ b/api/src/gfapi.map @@ -0,0 +1,283 @@ + +GFAPI_PRIVATE_3.4.0 { + global: + glfs_loc_touchup; + glfs_active_subvol; + glfs_subvol_done; + glfs_init_done; + glfs_resolve_at; + local: *; +}; + +GFAPI_3.4.0 { + global: + glfs_new; + glfs_set_volfile; + glfs_set_volfile_server; + glfs_set_logging; + glfs_init; + glfs_fini; + glfs_open; + glfs_creat; + glfs_close; + glfs_from_glfd; + glfs_set_xlator_option; + glfs_read; + glfs_write; + glfs_read_async; + glfs_write_async; + glfs_readv; + glfs_writev; + glfs_readv_async; + glfs_writev_async; + glfs_pread; + glfs_pwrite; + glfs_pread_async; + glfs_pwrite_async; + glfs_preadv; + glfs_pwritev; + glfs_preadv_async; + glfs_pwritev_async; + glfs_lseek; + glfs_ftruncate; + glfs_ftruncate_async; + glfs_lstat; + glfs_stat; + glfs_fstat; + glfs_fsync; + glfs_fsync_async; + glfs_fdatasync; + glfs_fdatasync_async; + glfs_access; + glfs_symlink; + glfs_readlink; + glfs_mknod; + glfs_mkdir; + glfs_unlink; + glfs_rmdir; + glfs_rename; + glfs_link; + glfs_opendir; + glfs_readdir_r; + glfs_readdirplus_r; + glfs_telldir; + glfs_seekdir; + glfs_closedir; + glfs_statvfs; + glfs_chmod; + glfs_fchmod; + glfs_chown; + glfs_lchown; + glfs_fchown; + glfs_utimens; + glfs_lutimens; + glfs_futimens; + glfs_getxattr; + glfs_lgetxattr; + glfs_fgetxattr; + glfs_listxattr; + glfs_llistxattr; + glfs_flistxattr; + glfs_setxattr; + glfs_lsetxattr; + glfs_fsetxattr; + glfs_removexattr; + glfs_lremovexattr; + glfs_fremovexattr; + glfs_getcwd; + glfs_chdir; + glfs_fchdir; + glfs_realpath; + glfs_posix_lock; + glfs_dup; +} GFAPI_PRIVATE_3.4.0; + +GFAPI_3.4.2 { + global: + glfs_setfsuid; + glfs_setfsgid; + glfs_setfsgroups; + glfs_h_creat; + glfs_h_mkdir; + glfs_h_mknod; + glfs_h_symlink; + glfs_h_unlink; + glfs_h_close; + glfs_h_truncate; + glfs_h_stat; + glfs_h_getattrs; + glfs_h_setattrs; + glfs_h_readlink; + glfs_h_link; + glfs_h_rename; + glfs_h_extract_handle; + glfs_h_create_from_handle; + glfs_h_opendir; + glfs_h_open; + glfs_h_lookupat; +} GFAPI_3.4.0; + +GFAPI_3.5.0 { + global: + glfs_get_volumeid; + glfs_readdir; + glfs_readdirplus; + glfs_fallocate; + glfs_discard; + glfs_discard_async; + glfs_zerofill; + glfs_zerofill_async; + glfs_caller_specific_init; + glfs_h_setxattrs; +} GFAPI_3.4.2; + +GFAPI_3.5.1 { + global: + glfs_unset_volfile_server; + glfs_h_getxattrs; + glfs_h_removexattrs; +} GFAPI_3.5.0; + +GFAPI_3.6.0 { + global: + glfs_get_volfile; + glfs_h_access; +} GFAPI_3.5.1; + +GFAPI_3.7.0 { + global: + glfs_h_poll_upcall; + glfs_h_acl_set; + glfs_h_acl_get; + glfs_h_statfs; + glfs_h_anonymous_read; + glfs_h_anonymous_write; +} GFAPI_3.6.0; + +GFAPI_PRIVATE_3.7.0 { + global: + glfs_free_from_ctx; + glfs_new_from_ctx; + glfs_resolve; + glfs_process_upcall_event; +} GFAPI_3.7.0; + +GFAPI_3.7.4 { + global: + glfs_h_lookupat; +} GFAPI_PRIVATE_3.7.0; + +GFAPI_3.7.15 { + global: + glfs_truncate; +} GFAPI_3.7.4; + +GFAPI_3.7.16 { + global: + glfs_free; + glfs_upcall_get_fs; + glfs_upcall_get_reason; + glfs_upcall_get_event; + glfs_upcall_inode_get_object; + glfs_upcall_inode_get_flags; + glfs_upcall_inode_get_stat; + glfs_upcall_inode_get_expire; + glfs_upcall_inode_get_pobject; + glfs_upcall_inode_get_pstat; + glfs_upcall_inode_get_oldpobject; + glfs_upcall_inode_get_oldpstat; + glfs_h_poll_upcall; +} GFAPI_3.7.15; + +GFAPI_3.7.17 { + global: + glfs_realpath; +} GFAPI_3.7.16; + +GFAPI_3.10.0 { + global: + glfs_sysrq; +} GFAPI_3.7.17; + +GFAPI_3.10.7 { + global: + glfs_fd_set_lkowner; +} GFAPI_3.10.0; + +GFAPI_3.11.0 { + glfs_xreaddirplus_r; + glfs_xreaddirplus_r_get_stat; + glfs_xreaddirplus_r_get_object; + glfs_object_copy; +} GFAPI_3.10.7; + +GFAPI_PRIVATE_3.12.0 { + global: + glfs_ipc; +} GFAPI_3.11.0; + +GFAPI_3.13.0 { + global: + glfs_upcall_register; + glfs_upcall_unregister; +} GFAPI_PRIVATE_3.12.0; + +GFAPI_4.0.0 { + global: + glfs_setfsleaseid; + glfs_file_lock; + glfs_lease; + glfs_h_lease; +} GFAPI_3.13.0; + +GFAPI_4.1.6 { + global: + glfs_upcall_lease_get_object; + glfs_upcall_lease_get_lease_type; +} GFAPI_4.0.0; + +GFAPI_PRIVATE_6.0 { + global: + glfs_statx; + glfs_iatt_from_statx; +} GFAPI_4.1.6; + +GFAPI_6.0 { + global: + glfs_read_async; + glfs_write_async; + glfs_readv_async; + glfs_writev_async; + glfs_pread; + glfs_pwrite; + glfs_pread_async; + glfs_pwrite_async; + glfs_preadv_async; + glfs_pwritev_async; + glfs_fsync; + glfs_fsync_async; + glfs_fdatasync; + glfs_fdatasync_async; + glfs_ftruncate; + glfs_ftruncate_async; + glfs_discard_async; + glfs_zerofill_async; + glfs_copy_file_range; + glfs_setattr; + glfs_fsetattr; +} GFAPI_PRIVATE_6.0; + +GFAPI_PRIVATE_6.1 { + global: + glfs_setfspid; +} GFAPI_6.0; + +GFAPI_6.6 { + global: + glfs_h_creat_open; +} GFAPI_PRIVATE_6.1; + +GFAPI_7.0 { + global: + glfs_set_statedump_path; +} GFAPI_6.6; diff --git a/api/src/glfs-fops.c b/api/src/glfs-fops.c index 66e7d69f14d..6aa3c5602d1 100644 --- a/api/src/glfs-fops.c +++ b/api/src/glfs-fops.c @@ -1,5 +1,6 @@ + /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,2919 +9,6437 @@ cases as published by the Free Software Foundation. */ +/* for SEEK_HOLE and SEEK_DATA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <unistd.h> #include "glfs-internal.h" #include "glfs-mem-types.h" -#include "syncop.h" +#include <glusterfs/syncop.h> #include "glfs.h" +#include "gfapi-messages.h" +#include <glusterfs/compat-errno.h> +#include <limits.h> +#include "glusterfs3.h" +#include <glusterfs/iatt.h> + +#ifdef NAME_MAX +#define GF_NAME_MAX NAME_MAX +#else +#define GF_NAME_MAX 255 +#endif -#define DEFAULT_REVAL_COUNT 1 +struct upcall_syncop_args { + struct glfs *fs; + struct gf_upcall upcall_data; +}; -#define ESTALE_RETRY(ret,errno,reval,loc,label) do { \ - if (ret == -1 && errno == ESTALE) { \ - if (reval < DEFAULT_REVAL_COUNT) { \ - reval++; \ - loc_wipe (loc); \ - goto label; \ - } \ - } \ - } while (0) +#define READDIRBUF_SIZE (sizeof(struct dirent) + GF_NAME_MAX + 1) +typedef void (*glfs_io_cbk34)(glfs_fd_t *fd, ssize_t ret, void *data); -static int -glfs_loc_link (loc_t *loc, struct iatt *iatt) +/* + * This function will mark glfd for deletion and decrement its refcount. + */ +int +glfs_mark_glfd_for_deletion(struct glfs_fd *glfd) { - int ret = -1; - inode_t *linked_inode = NULL; + LOCK(&glfd->lock); + { + glfd->state = GLFD_CLOSE; + } + UNLOCK(&glfd->lock); - if (!loc->inode) { - errno = EINVAL; - return -1; - } + GF_REF_PUT(glfd); - linked_inode = inode_link (loc->inode, loc->parent, loc->name, iatt); - if (linked_inode) { - inode_lookup (linked_inode); - inode_unref (linked_inode); - ret = 0; - } else { - ret = -1; - errno = ENOMEM; - } - - return ret; + return 0; } - -static void -glfs_iatt_to_stat (struct glfs *fs, struct iatt *iatt, struct stat *stat) +/* This function is useful for all async fops. There is chance that glfd is + * closed before async fop is completed. When glfd is closed we change the + * state to GLFD_CLOSE. + * + * This function will return _gf_true if the glfd is still valid else return + * _gf_false. + */ +gf_boolean_t +glfs_is_glfd_still_valid(struct glfs_fd *glfd) { - iatt_to_stat (iatt, stat); - stat->st_dev = fs->dev_id; -} + gf_boolean_t ret = _gf_false; + LOCK(&glfd->lock); + { + if (glfd->state != GLFD_CLOSE) + ret = _gf_true; + } + UNLOCK(&glfd->lock); -static int -glfs_loc_unlink (loc_t *loc) + return ret; +} + +void +glfd_set_state_bind(struct glfs_fd *glfd) { - inode_unlink (loc->inode, loc->parent, loc->name); + LOCK(&glfd->lock); + { + glfd->state = GLFD_OPEN; + } + UNLOCK(&glfd->lock); - return 0; -} + fd_bind(glfd->fd); + glfs_fd_bind(glfd); + return; +} -struct glfs_fd * -glfs_open (struct glfs *fs, const char *path, int flags) +/* + * This routine is called when an upcall event of type + * 'GF_UPCALL_CACHE_INVALIDATION' is received. + * It makes a copy of the contents of the upcall cache-invalidation + * data received into an entry which is stored in the upcall list + * maintained by gfapi. + */ +int +glfs_get_upcall_cache_invalidation(struct gf_upcall *to_up_data, + struct gf_upcall *from_up_data) { - int ret = -1; - struct glfs_fd *glfd = NULL; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + struct gf_upcall_cache_invalidation *ca_data = NULL; + struct gf_upcall_cache_invalidation *f_ca_data = NULL; + int ret = -1; - __glfs_entry_fs (fs); + GF_VALIDATE_OR_GOTO(THIS->name, to_up_data, out); + GF_VALIDATE_OR_GOTO(THIS->name, from_up_data, out); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + f_ca_data = from_up_data->data; + GF_VALIDATE_OR_GOTO(THIS->name, f_ca_data, out); - glfd = glfs_fd_new (fs); - if (!glfd) - goto out; + ca_data = GF_CALLOC(1, sizeof(*ca_data), glfs_mt_upcall_entry_t); -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + if (!ca_data) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } + + to_up_data->data = ca_data; + + ca_data->flags = f_ca_data->flags; + ca_data->expire_time_attr = f_ca_data->expire_time_attr; + ca_data->stat = f_ca_data->stat; + ca_data->p_stat = f_ca_data->p_stat; + ca_data->oldp_stat = f_ca_data->oldp_stat; - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ret = 0; +out: + return ret; +} + +int +glfs_get_upcall_lease(struct gf_upcall *to_up_data, + struct gf_upcall *from_up_data) +{ + struct gf_upcall_recall_lease *ca_data = NULL; + struct gf_upcall_recall_lease *f_ca_data = NULL; + int ret = -1; - if (ret) - goto out; + GF_VALIDATE_OR_GOTO(THIS->name, to_up_data, out); + GF_VALIDATE_OR_GOTO(THIS->name, from_up_data, out); - if (IA_ISDIR (iatt.ia_type)) { - ret = -1; - errno = EISDIR; - goto out; - } + f_ca_data = from_up_data->data; + GF_VALIDATE_OR_GOTO(THIS->name, f_ca_data, out); - if (!IA_ISREG (iatt.ia_type)) { - ret = -1; - errno = EINVAL; - goto out; - } + ca_data = GF_CALLOC(1, sizeof(*ca_data), glfs_mt_upcall_entry_t); - if (glfd->fd) { - /* Retry. Safe to touch glfd->fd as we - still have not glfs_fd_bind() yet. - */ - fd_unref (glfd->fd); - glfd->fd = NULL; - } + if (!ca_data) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } - glfd->fd = fd_create (loc.inode, getpid()); - if (!glfd->fd) { - ret = -1; - errno = ENOMEM; - goto out; - } + to_up_data->data = ca_data; - ret = syncop_open (subvol, &loc, flags, glfd->fd); + ca_data->lease_type = f_ca_data->lease_type; + gf_uuid_copy(ca_data->tid, f_ca_data->tid); + ca_data->dict = f_ca_data->dict; - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ret = 0; out: - loc_wipe (&loc); + return ret; +} +int +glfs_loc_link(loc_t *loc, struct iatt *iatt) +{ + int ret = -1; + inode_t *old_inode = NULL; + uint64_t ctx_value = LOOKUP_NOT_NEEDED; + + if (!loc->inode) { + errno = EINVAL; + return -1; + } + + old_inode = loc->inode; + + /* If the inode already exists in the cache, the inode + * returned here points to the existing one. We need + * to update loc.inode accordingly. + */ + loc->inode = inode_link(loc->inode, loc->parent, loc->name, iatt); + if (loc->inode) { + inode_ctx_set(loc->inode, THIS, &ctx_value); + inode_lookup(loc->inode); + inode_unref(old_inode); + ret = 0; + } else { + ret = -1; + } + + return ret; +} - if (ret && glfd) { - glfs_fd_destroy (glfd); - glfd = NULL; - } else { - fd_bind (glfd->fd); - glfs_fd_bind (glfd); - } +void +glfs_iatt_to_stat(struct glfs *fs, struct iatt *iatt, struct stat *stat) +{ + iatt_to_stat(iatt, stat); + stat->st_dev = fs->dev_id; +} - glfs_subvol_done (fs, subvol); +void +glfs_iatt_to_statx(struct glfs *fs, const struct iatt *iatt, + struct glfs_stat *statx) +{ + statx->glfs_st_mask = 0; + + statx->glfs_st_mode = 0; + if (IATT_TYPE_VALID(iatt->ia_flags)) { + statx->glfs_st_mode |= st_mode_type_from_ia(iatt->ia_type); + statx->glfs_st_mask |= GLFS_STAT_TYPE; + } + + if (IATT_MODE_VALID(iatt->ia_flags)) { + statx->glfs_st_mode |= st_mode_prot_from_ia(iatt->ia_prot); + statx->glfs_st_mask |= GLFS_STAT_MODE; + } + + if (IATT_NLINK_VALID(iatt->ia_flags)) { + statx->glfs_st_nlink = iatt->ia_nlink; + statx->glfs_st_mask |= GLFS_STAT_NLINK; + } + + if (IATT_UID_VALID(iatt->ia_flags)) { + statx->glfs_st_uid = iatt->ia_uid; + statx->glfs_st_mask |= GLFS_STAT_UID; + } + + if (IATT_GID_VALID(iatt->ia_flags)) { + statx->glfs_st_gid = iatt->ia_gid; + statx->glfs_st_mask |= GLFS_STAT_GID; + } + + if (IATT_ATIME_VALID(iatt->ia_flags)) { + statx->glfs_st_atime.tv_sec = iatt->ia_atime; + statx->glfs_st_atime.tv_nsec = iatt->ia_atime_nsec; + statx->glfs_st_mask |= GLFS_STAT_ATIME; + } + + if (IATT_MTIME_VALID(iatt->ia_flags)) { + statx->glfs_st_mtime.tv_sec = iatt->ia_mtime; + statx->glfs_st_mtime.tv_nsec = iatt->ia_mtime_nsec; + statx->glfs_st_mask |= GLFS_STAT_MTIME; + } + + if (IATT_CTIME_VALID(iatt->ia_flags)) { + statx->glfs_st_ctime.tv_sec = iatt->ia_ctime; + statx->glfs_st_ctime.tv_nsec = iatt->ia_ctime_nsec; + statx->glfs_st_mask |= GLFS_STAT_CTIME; + } + + if (IATT_BTIME_VALID(iatt->ia_flags)) { + statx->glfs_st_btime.tv_sec = iatt->ia_btime; + statx->glfs_st_btime.tv_nsec = iatt->ia_btime_nsec; + statx->glfs_st_mask |= GLFS_STAT_BTIME; + } + + if (IATT_INO_VALID(iatt->ia_flags)) { + statx->glfs_st_ino = iatt->ia_ino; + statx->glfs_st_mask |= GLFS_STAT_INO; + } + + if (IATT_SIZE_VALID(iatt->ia_flags)) { + statx->glfs_st_size = iatt->ia_size; + statx->glfs_st_mask |= GLFS_STAT_SIZE; + } + + if (IATT_BLOCKS_VALID(iatt->ia_flags)) { + statx->glfs_st_blocks = iatt->ia_blocks; + statx->glfs_st_mask |= GLFS_STAT_BLOCKS; + } + + /* unconditionally present, encode as is */ + statx->glfs_st_blksize = iatt->ia_blksize; + statx->glfs_st_rdev_major = ia_major(iatt->ia_rdev); + statx->glfs_st_rdev_minor = ia_minor(iatt->ia_rdev); + statx->glfs_st_dev_major = ia_major(fs->dev_id); + statx->glfs_st_dev_minor = ia_minor(fs->dev_id); + + /* At present we do not read any localFS attributes and pass them along, + * so setting this to 0. As we start supporting file attributes we can + * populate the same here as well */ + statx->glfs_st_attributes = 0; + statx->glfs_st_attributes_mask = 0; +} - return glfd; +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_iatt_from_statx, 6.0) +void +priv_glfs_iatt_from_statx(struct iatt *iatt, const struct glfs_stat *statx) +{ + /* Most code in xlators are not checking validity flags before accessing + the items. Hence zero everything before setting valid items */ + memset(iatt, 0, sizeof(struct iatt)); + + if (GLFS_STAT_TYPE_VALID(statx->glfs_st_mask)) { + iatt->ia_type = ia_type_from_st_mode(statx->glfs_st_mode); + iatt->ia_flags |= IATT_TYPE; + } + + if (GLFS_STAT_MODE_VALID(statx->glfs_st_mask)) { + iatt->ia_prot = ia_prot_from_st_mode(statx->glfs_st_mode); + iatt->ia_flags |= IATT_MODE; + } + + if (GLFS_STAT_NLINK_VALID(statx->glfs_st_mask)) { + iatt->ia_nlink = statx->glfs_st_nlink; + iatt->ia_flags |= IATT_NLINK; + } + + if (GLFS_STAT_UID_VALID(statx->glfs_st_mask)) { + iatt->ia_uid = statx->glfs_st_uid; + iatt->ia_flags |= IATT_UID; + } + + if (GLFS_STAT_GID_VALID(statx->glfs_st_mask)) { + iatt->ia_gid = statx->glfs_st_gid; + iatt->ia_flags |= IATT_GID; + } + + if (GLFS_STAT_ATIME_VALID(statx->glfs_st_mask)) { + iatt->ia_atime = statx->glfs_st_atime.tv_sec; + iatt->ia_atime_nsec = statx->glfs_st_atime.tv_nsec; + iatt->ia_flags |= IATT_ATIME; + } + + if (GLFS_STAT_MTIME_VALID(statx->glfs_st_mask)) { + iatt->ia_mtime = statx->glfs_st_mtime.tv_sec; + iatt->ia_mtime_nsec = statx->glfs_st_mtime.tv_nsec; + iatt->ia_flags |= IATT_MTIME; + } + + if (GLFS_STAT_CTIME_VALID(statx->glfs_st_mask)) { + iatt->ia_ctime = statx->glfs_st_ctime.tv_sec; + iatt->ia_ctime_nsec = statx->glfs_st_ctime.tv_nsec; + iatt->ia_flags |= IATT_CTIME; + } + + if (GLFS_STAT_BTIME_VALID(statx->glfs_st_mask)) { + iatt->ia_btime = statx->glfs_st_btime.tv_sec; + iatt->ia_btime_nsec = statx->glfs_st_btime.tv_nsec; + iatt->ia_flags |= IATT_BTIME; + } + + if (GLFS_STAT_INO_VALID(statx->glfs_st_mask)) { + iatt->ia_ino = statx->glfs_st_ino; + iatt->ia_flags |= IATT_INO; + } + + if (GLFS_STAT_SIZE_VALID(statx->glfs_st_mask)) { + iatt->ia_size = statx->glfs_st_size; + iatt->ia_flags |= IATT_SIZE; + } + + if (GLFS_STAT_BLOCKS_VALID(statx->glfs_st_mask)) { + iatt->ia_blocks = statx->glfs_st_blocks; + iatt->ia_flags |= IATT_BLOCKS; + } + + /* unconditionally present, encode as is */ + iatt->ia_blksize = statx->glfs_st_blksize; + iatt->ia_rdev = makedev(statx->glfs_st_rdev_major, + statx->glfs_st_rdev_minor); + iatt->ia_dev = makedev(statx->glfs_st_dev_major, statx->glfs_st_dev_minor); + iatt->ia_attributes = statx->glfs_st_attributes; + iatt->ia_attributes_mask = statx->glfs_st_attributes_mask; } +void +glfsflags_from_gfapiflags(struct glfs_stat *stat, int *glvalid) +{ + *glvalid = 0; + if (stat->glfs_st_mask & GLFS_STAT_MODE) { + *glvalid |= GF_SET_ATTR_MODE; + } + + if (stat->glfs_st_mask & GLFS_STAT_SIZE) { + *glvalid |= GF_SET_ATTR_SIZE; + } + + if (stat->glfs_st_mask & GLFS_STAT_UID) { + *glvalid |= GF_SET_ATTR_UID; + } + + if (stat->glfs_st_mask & GLFS_STAT_GID) { + *glvalid |= GF_SET_ATTR_GID; + } + + if (stat->glfs_st_mask & GLFS_STAT_ATIME) { + *glvalid |= GF_SET_ATTR_ATIME; + } + + if (stat->glfs_st_mask & GLFS_STAT_MTIME) { + *glvalid |= GF_SET_ATTR_MTIME; + } +} int -glfs_close (struct glfs_fd *glfd) +glfs_loc_unlink(loc_t *loc) { - xlator_t *subvol = NULL; - int ret = -1; - fd_t *fd = NULL; - struct glfs *fs = NULL; + inode_unlink(loc->inode, loc->parent, loc->name); - __glfs_entry_fd (glfd); + /* since glfs_h_* objects hold a reference to inode + * it is safe to keep lookup count to '0' */ + if (!inode_has_dentry(loc->inode)) + inode_forget(loc->inode, 0); - subvol = glfs_active_subvol (glfd->fs); + return 0; +} - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_open, 3.4.0) +struct glfs_fd * +pub_glfs_open(struct glfs *fs, const char *path, int flags) +{ + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + glfd = glfs_fd_new(fs); + if (!glfd) + goto out; - ret = syncop_flush (subvol, fd); +retry: + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); + + if (ret) + goto out; + + if (IA_ISDIR(iatt.ia_type)) { + ret = -1; + errno = EISDIR; + goto out; + } + + if (!IA_ISREG(iatt.ia_type)) { + ret = -1; + errno = EINVAL; + goto out; + } + + if (glfd->fd) { + /* Retry. Safe to touch glfd->fd as we + still have not glfs_fd_bind() yet. + */ + fd_unref(glfd->fd); + glfd->fd = NULL; + } + + glfd->fd = fd_create(loc.inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + glfd->fd->flags = flags; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_open(subvol, &loc, flags, glfd->fd, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); out: - fs = glfd->fs; - glfs_fd_destroy (glfd); + loc_wipe(&loc); - if (fd) - fd_unref (fd); + if (fop_attr) + dict_unref(fop_attr); - glfs_subvol_done (fs, subvol); + if (ret && glfd) { + GF_REF_PUT(glfd); + glfd = NULL; + } else if (glfd) { + glfd_set_state_bind(glfd); + } - return ret; -} + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; +invalid_fs: + return glfd; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_close, 3.4.0) int -glfs_lstat (struct glfs *fs, const char *path, struct stat *stat) +pub_glfs_close(struct glfs_fd *glfd) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + xlator_t *subvol = NULL; + int ret = -1; + fd_t *fd = NULL; + struct glfs *fs = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + if (glfd->lk_owner.len != 0) { + ret = syncopctx_setfslkowner(&glfd->lk_owner); + if (ret) + goto out; + } + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_flush(subvol, fd, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); +out: + fs = glfd->fs; + + if (fd) + fd_unref(fd); + if (fop_attr) + dict_unref(fop_attr); - __glfs_entry_fs (fs); + glfs_mark_glfd_for_deletion(glfd); + glfs_subvol_done(fs, subvol); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lstat, 3.4.0) +int +pub_glfs_lstat(struct glfs *fs, const char *path, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0 && stat) - glfs_iatt_to_stat (fs, &iatt, stat); + if (ret == 0 && stat) + glfs_iatt_to_stat(fs, &iatt, stat); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_stat, 3.4.0) int -glfs_stat (struct glfs *fs, const char *path, struct stat *stat) +pub_glfs_stat(struct glfs *fs, const char *path, struct stat *stat) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0 && stat) - glfs_iatt_to_stat (fs, &iatt, stat); + if (ret == 0 && stat) + glfs_iatt_to_stat(fs, &iatt, stat); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_statx, 6.0) int -glfs_fstat (struct glfs_fd *glfd, struct stat *stat) +priv_glfs_statx(struct glfs *fs, const char *path, const unsigned int mask, + struct glfs_stat *statxbuf) { - int ret = -1; - xlator_t *subvol = NULL; - struct iatt iatt = {0, }; - fd_t *fd = NULL; + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (path == NULL) { + ret = -1; + errno = EINVAL; + goto out; + } + + if (mask & ~GLFS_STAT_ALL) { + ret = -1; + errno = EINVAL; + goto out; + } + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + +retry: + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - __glfs_entry_fd (glfd); + if (ret == 0 && statxbuf) + glfs_iatt_to_statx(fs, &iatt, statxbuf); +out: + loc_wipe(&loc); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + glfs_subvol_done(fs, subvol); - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + __GLFS_EXIT_FS; - ret = syncop_fstat (subvol, fd, &iatt); +invalid_fs: + return ret; +} - if (ret == 0 && stat) - glfs_iatt_to_stat (glfd->fs, &iatt, stat); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fstat, 3.4.0) +int +pub_glfs_fstat(struct glfs_fd *glfd, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + struct iatt iatt = { + 0, + }; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = syncop_fstat(subvol, fd, &iatt, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret == 0 && stat) + glfs_iatt_to_stat(glfd->fs, &iatt, stat); out: - if (fd) - fd_unref (fd); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); - glfs_subvol_done (glfd->fs, subvol); + glfs_subvol_done(glfd->fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_creat, 3.4.0) struct glfs_fd * -glfs_creat (struct glfs *fs, const char *path, int flags, mode_t mode) -{ - int ret = -1; - struct glfs_fd *glfd = NULL; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - uuid_t gfid; - dict_t *xattr_req = NULL; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } - - xattr_req = dict_new (); - if (!xattr_req) { - ret = -1; - errno = ENOMEM; - goto out; - } - - uuid_generate (gfid); - ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16); - if (ret) { - ret = -1; - errno = ENOMEM; - goto out; - } - - glfd = glfs_fd_new (fs); - if (!glfd) - goto out; - - /* This must be glfs_resolve() and NOT glfs_lresolve(). - That is because open("name", O_CREAT) where "name" - is a danging symlink must create the dangling - destinataion. - */ +pub_glfs_creat(struct glfs *fs, const char *path, int flags, mode_t mode) +{ + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + glfd = glfs_fd_new(fs); + if (!glfd) + goto out; + + /* This must be glfs_resolve() and NOT glfs_lresolve(). + That is because open("name", O_CREAT) where "name" + is a danging symlink must create the dangling + destination. + */ retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); - - ESTALE_RETRY (ret, errno, reval, &loc, retry); - - if (ret == -1 && errno != ENOENT) - /* Any other type of error is fatal */ - goto out; - - if (ret == -1 && errno == ENOENT && !loc.parent) - /* The parent directory or an ancestor even - higher does not exist - */ - goto out; - - if (loc.inode) { - if (flags & O_EXCL) { - ret = -1; - errno = EEXIST; - goto out; - } - - if (IA_ISDIR (iatt.ia_type)) { - ret = -1; - errno = EISDIR; - goto out; - } - - if (!IA_ISREG (iatt.ia_type)) { - ret = -1; - errno = EINVAL; - goto out; - } - } - - if (ret == -1 && errno == ENOENT) { - loc.inode = inode_new (loc.parent->table); - if (!loc.inode) { - ret = -1; - errno = ENOMEM; - goto out; - } - } - - if (glfd->fd) { - /* Retry. Safe to touch glfd->fd as we - still have not glfs_fd_bind() yet. - */ - fd_unref (glfd->fd); - glfd->fd = NULL; - } - - glfd->fd = fd_create (loc.inode, getpid()); - if (!glfd->fd) { - ret = -1; - errno = ENOMEM; - goto out; - } - - ret = syncop_create (subvol, &loc, flags, mode, glfd->fd, - xattr_req, &iatt); - - ESTALE_RETRY (ret, errno, reval, &loc, retry); - - if (ret == 0) - ret = glfs_loc_link (&loc, &iatt); -out: - loc_wipe (&loc); - - if (xattr_req) - dict_unref (xattr_req); - - if (ret && glfd) { - glfs_fd_destroy (glfd); - glfd = NULL; - } else { - fd_bind (glfd->fd); - glfs_fd_bind (glfd); - } - - glfs_subvol_done (fs, subvol); - - return glfd; -} + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); + + if (ret == -1 && errno != ENOENT) + /* Any other type of error is fatal */ + goto out; + + if (ret == -1 && errno == ENOENT && !loc.parent) + /* The parent directory or an ancestor even + higher does not exist + */ + goto out; + + if (loc.inode) { + if (flags & O_EXCL) { + ret = -1; + errno = EEXIST; + goto out; + } + + if (IA_ISDIR(iatt.ia_type)) { + ret = -1; + errno = EISDIR; + goto out; + } + + if (!IA_ISREG(iatt.ia_type)) { + ret = -1; + errno = EINVAL; + goto out; + } + } + + if (ret == -1 && errno == ENOENT) { + loc.inode = inode_new(loc.parent->table); + if (!loc.inode) { + ret = -1; + errno = ENOMEM; + goto out; + } + } + + if (glfd->fd) { + /* Retry. Safe to touch glfd->fd as we + still have not glfs_fd_bind() yet. + */ + fd_unref(glfd->fd); + glfd->fd = NULL; + } + + glfd->fd = fd_create(loc.inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + glfd->fd->flags = flags; + + if (get_fop_attr_thrd_key(&xattr_req)) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + if (ret == 0) { + ret = syncop_open(subvol, &loc, flags, glfd->fd, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + } else { + ret = syncop_create(subvol, &loc, flags, mode, glfd->fd, &iatt, + xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + } + + ESTALE_RETRY(ret, errno, reval, &loc, retry); + + if (ret == 0) + ret = glfs_loc_link(&loc, &iatt); +out: + loc_wipe(&loc); + if (xattr_req) + dict_unref(xattr_req); -off_t -glfs_lseek (struct glfs_fd *glfd, off_t offset, int whence) -{ - struct stat sb = {0, }; - int ret = -1; + if (ret && glfd) { + GF_REF_PUT(glfd); + glfd = NULL; + } else if (glfd) { + glfd_set_state_bind(glfd); + } - __glfs_entry_fd (glfd); + glfs_subvol_done(fs, subvol); - switch (whence) { - case SEEK_SET: - glfd->offset = offset; - break; - case SEEK_CUR: - glfd->offset += offset; - break; - case SEEK_END: - ret = glfs_fstat (glfd, &sb); - if (ret) { - /* seek cannot fail :O */ - break; - } - glfd->offset = sb.st_size + offset; - break; - } + __GLFS_EXIT_FS; - return glfd->offset; +invalid_fs: + return glfd; } +#ifdef HAVE_SEEK_HOLE +static int +glfs_seek(struct glfs_fd *glfd, off_t offset, int whence) +{ + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + gf_seek_what_t what = 0; + off_t off = -1; + + switch (whence) { + case SEEK_DATA: + what = GF_SEEK_DATA; + break; + case SEEK_HOLE: + what = GF_SEEK_HOLE; + break; + default: + /* other SEEK_* do not make sense, all operations get an offset + * and the position in the fd is not tracked */ + errno = EINVAL; + goto out; + } + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto done; + } + + ret = syncop_seek(subvol, fd, offset, what, NULL, &off); + DECODE_SYNCOP_ERR(ret); + + if (ret != -1) + glfd->offset = off; + +done: + if (fd) + fd_unref(fd); + + glfs_subvol_done(glfd->fs, subvol); -////////////// +out: + return ret; +} +#endif -ssize_t -glfs_preadv (struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, - off_t offset, int flags) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lseek, 3.4.0) +off_t +pub_glfs_lseek(struct glfs_fd *glfd, off_t offset, int whence) { - xlator_t *subvol = NULL; - int ret = -1; - size_t size = -1; - struct iovec *iov = NULL; - int cnt = 0; - struct iobref *iobref = NULL; - fd_t *fd = NULL; - - __glfs_entry_fd (glfd); + struct stat sb = { + 0, + }; + int ret = -1; + off_t off = -1; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + switch (whence) { + case SEEK_SET: + glfd->offset = offset; + ret = 0; + break; + case SEEK_CUR: + glfd->offset += offset; + ret = 0; + break; + case SEEK_END: + ret = pub_glfs_fstat(glfd, &sb); + if (ret) { + /* seek cannot fail :O */ + break; + } + glfd->offset = sb.st_size + offset; + break; +#ifdef HAVE_SEEK_HOLE + case SEEK_DATA: + case SEEK_HOLE: + ret = glfs_seek(glfd, offset, whence); + break; +#endif + default: + errno = EINVAL; + } - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (glfd) + GF_REF_PUT(glfd); - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + __GLFS_EXIT_FS; - size = iov_length (iovec, iovcnt); + if (ret != -1) + off = glfd->offset; - ret = syncop_readv (subvol, fd, size, offset, 0, &iov, &cnt, &iobref); - if (ret <= 0) - goto out; + return off; - size = iov_copy (iovec, iovcnt, iov, cnt); /* FIXME!!! */ +invalid_fs: + return -1; +} - glfd->offset = (offset + size); +static ssize_t +glfs_preadv_common(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, + off_t offset, int flags, struct glfs_stat *poststat) +{ + xlator_t *subvol = NULL; + ssize_t ret = -1; + ssize_t size = -1; + struct iovec *iov = NULL; + int cnt = 0; + struct iobref *iobref = NULL; + fd_t *fd = NULL; + struct iatt iatt = { + 0, + }; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + size = iov_length(iovec, iovcnt); + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_readv(subvol, fd, size, offset, 0, &iov, &cnt, &iobref, &iatt, + fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0 && poststat) + glfs_iatt_to_statx(glfd->fs, &iatt, poststat); + + if (ret <= 0) + goto out; + + size = iov_copy(iovec, iovcnt, iov, cnt); /* FIXME!!! */ + + glfd->offset = (offset + size); + + ret = size; +out: + if (iov) + GF_FREE(iov); + if (iobref) + iobref_unref(iobref); - if (iov) - GF_FREE (iov); - if (iobref) - iobref_unref (iobref); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); -out: - if (fd) - fd_unref (fd); + glfs_subvol_done(glfd->fs, subvol); - glfs_subvol_done (glfd->fs, subvol); + __GLFS_EXIT_FS; - return size; +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_preadv, 3.4.0) +ssize_t +pub_glfs_preadv(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, + off_t offset, int flags) +{ + return glfs_preadv_common(glfd, iovec, iovcnt, offset, flags, NULL); +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_read, 3.4.0) ssize_t -glfs_read (struct glfs_fd *glfd, void *buf, size_t count, int flags) +pub_glfs_read(struct glfs_fd *glfd, void *buf, size_t count, int flags) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = buf; - iov.iov_len = count; + if (glfd == NULL) { + errno = EBADF; + return -1; + } - ret = glfs_preadv (glfd, &iov, 1, glfd->offset, flags); + iov.iov_base = buf; + iov.iov_len = count; - return ret; -} + ret = pub_glfs_preadv(glfd, &iov, 1, glfd->offset, flags); + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_pread34, glfs_pread, 3.4.0) ssize_t -glfs_pread (struct glfs_fd *glfd, void *buf, size_t count, off_t offset, - int flags) +pub_glfs_pread34(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, + int flags) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = buf; - iov.iov_len = count; + iov.iov_base = buf; + iov.iov_len = count; - ret = glfs_preadv (glfd, &iov, 1, offset, flags); + ret = pub_glfs_preadv(glfd, &iov, 1, offset, flags); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pread, 6.0) ssize_t -glfs_readv (struct glfs_fd *glfd, const struct iovec *iov, int count, - int flags) +pub_glfs_pread(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, + int flags, struct glfs_stat *poststat) { - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; + + iov.iov_base = buf; + iov.iov_len = count; - ret = glfs_preadv (glfd, iov, count, glfd->offset, flags); + ret = glfs_preadv_common(glfd, &iov, 1, offset, flags, poststat); - return ret; + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readv, 3.4.0) +ssize_t +pub_glfs_readv(struct glfs_fd *glfd, const struct iovec *iov, int count, + int flags) +{ + ssize_t ret = 0; + + if (glfd == NULL) { + errno = EBADF; + return -1; + } + + ret = pub_glfs_preadv(glfd, iov, count, glfd->offset, flags); + + return ret; +} struct glfs_io { - struct glfs_fd *glfd; - int op; - off_t offset; - struct iovec *iov; - int count; - int flags; - glfs_io_cbk fn; - void *data; + struct glfs_fd *glfd; + int op; + off_t offset; + struct iovec *iov; + int count; + int flags; + gf_boolean_t oldcb; + union { + glfs_io_cbk34 fn34; + glfs_io_cbk fn; + }; + void *data; }; - static int -glfs_io_async_cbk (int ret, call_frame_t *frame, void *data) +glfs_io_async_cbk(int op_ret, int op_errno, call_frame_t *frame, void *cookie, + struct iovec *iovec, int count, struct iatt *prebuf, + struct iatt *postbuf) { - struct glfs_io *gio = data; + struct glfs_io *gio = NULL; + xlator_t *subvol = NULL; + struct glfs *fs = NULL; + struct glfs_fd *glfd = NULL; + int ret = -1; + struct glfs_stat prestat = {}, *prestatp = NULL; + struct glfs_stat poststat = {}, *poststatp = NULL; + + GF_VALIDATE_OR_GOTO("gfapi", frame, inval); + GF_VALIDATE_OR_GOTO("gfapi", cookie, inval); + + gio = frame->local; + frame->local = NULL; + subvol = cookie; + glfd = gio->glfd; + fs = glfd->fs; + + if (!glfs_is_glfd_still_valid(glfd)) + goto err; + + if (op_ret <= 0) { + goto out; + } else if (gio->op == GF_FOP_READ) { + if (!iovec) { + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + op_ret = iov_copy(gio->iov, gio->count, iovec, count); + glfd->offset = gio->offset + op_ret; + } else if (gio->op == GF_FOP_WRITE) { + glfd->offset = gio->offset + gio->iov->iov_len; + } - gio->fn (gio->glfd, ret, gio->data); +out: + errno = op_errno; + if (gio->oldcb) { + gio->fn34(gio->glfd, op_ret, gio->data); + } else { + if (prebuf) { + prestatp = &prestat; + glfs_iatt_to_statx(fs, prebuf, prestatp); + } + + if (postbuf) { + poststatp = &poststat; + glfs_iatt_to_statx(fs, postbuf, poststatp); + } + + gio->fn(gio->glfd, op_ret, prestatp, poststatp, gio->data); + } +err: + fd_unref(glfd->fd); + /* Since the async operation is complete + * release the ref taken during the start + * of async operation + */ + GF_REF_PUT(glfd); + + GF_FREE(gio->iov); + GF_FREE(gio); + STACK_DESTROY(frame->root); + glfs_subvol_done(fs, subvol); + + ret = 0; +inval: + return ret; +} - GF_FREE (gio->iov); - GF_FREE (gio); +static int +glfs_preadv_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iovec *iovec, int count, + struct iatt *stbuf, struct iobref *iobref, dict_t *xdata) +{ + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, iovec, count, NULL, + stbuf); - return 0; + return 0; } - static int -glfs_io_async_task (void *data) +glfs_preadv_async_common(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, gf_boolean_t oldcb, + glfs_io_cbk fn, void *data) { - struct glfs_io *gio = data; - ssize_t ret = 0; + struct glfs_io *gio = NULL; + int ret = 0; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + struct glfs *fs = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + fs = glfd->fs; + + frame = syncop_create_frame(THIS); + if (!frame) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gio->iov = iov_dup(iovec, count); + if (!gio->iov) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gio->op = GF_FOP_READ; + gio->glfd = glfd; + gio->count = count; + gio->offset = offset; + gio->flags = flags; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + + frame->local = gio; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + STACK_WIND_COOKIE(frame, glfs_preadv_async_cbk, subvol, subvol, + subvol->fops->readv, fd, iov_length(iovec, count), offset, + flags, fop_attr); - switch (gio->op) { - case GF_FOP_READ: - ret = glfs_preadv (gio->glfd, gio->iov, gio->count, - gio->offset, gio->flags); - break; - case GF_FOP_WRITE: - ret = glfs_pwritev (gio->glfd, gio->iov, gio->count, - gio->offset, gio->flags); - break; - case GF_FOP_FTRUNCATE: - ret = glfs_ftruncate (gio->glfd, gio->offset); - break; - case GF_FOP_FSYNC: - if (gio->flags) - ret = glfs_fdatasync (gio->glfd); - else - ret = glfs_fsync (gio->glfd); - break; - } +out: + if (ret) { + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (gio) { + GF_FREE(gio->iov); + GF_FREE(gio); + } + if (frame) { + STACK_DESTROY(frame->root); + } + glfs_subvol_done(fs, subvol); + } + if (fop_attr) + dict_unref(fop_attr); + + __GLFS_EXIT_FS; + + return ret; + +invalid_fs: + return -1; +} - return (int) ret; +GFAPI_SYMVER_PUBLIC(glfs_preadv_async34, glfs_preadv_async, 3.4.0) +int +pub_glfs_preadv_async34(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, glfs_io_cbk34 fn, + void *data) +{ + return glfs_preadv_async_common(glfd, iovec, count, offset, flags, _gf_true, + (void *)fn, data); } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_preadv_async, 6.0) +int +pub_glfs_preadv_async(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, glfs_io_cbk fn, + void *data) +{ + return glfs_preadv_async_common(glfd, iovec, count, offset, flags, + _gf_false, fn, data); +} +GFAPI_SYMVER_PUBLIC(glfs_read_async34, glfs_read_async, 3.4.0) int -glfs_preadv_async (struct glfs_fd *glfd, const struct iovec *iovec, int count, - off_t offset, int flags, glfs_io_cbk fn, void *data) +pub_glfs_read_async34(struct glfs_fd *glfd, void *buf, size_t count, int flags, + glfs_io_cbk34 fn, void *data) { - struct glfs_io *gio = NULL; - int ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; + + if (glfd == NULL) { + errno = EBADF; + return -1; + } + + iov.iov_base = buf; + iov.iov_len = count; - gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t); - if (!gio) { - errno = ENOMEM; - return -1; - } + ret = glfs_preadv_async_common(glfd, &iov, 1, glfd->offset, flags, _gf_true, + (void *)fn, data); - gio->iov = iov_dup (iovec, count); - if (!gio->iov) { - GF_FREE (gio); - errno = ENOMEM; - return -1; - } + return ret; +} - gio->op = GF_FOP_READ; - gio->glfd = glfd; - gio->count = count; - gio->offset = offset; - gio->flags = flags; - gio->fn = fn; - gio->data = data; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_read_async, 6.0) +int +pub_glfs_read_async(struct glfs_fd *glfd, void *buf, size_t count, int flags, + glfs_io_cbk fn, void *data) +{ + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - ret = synctask_new (glfs_from_glfd (glfd)->ctx->env, - glfs_io_async_task, glfs_io_async_cbk, - NULL, gio); + if (glfd == NULL) { + errno = EBADF; + return -1; + } - if (ret) { - GF_FREE (gio->iov); - GF_FREE (gio); - } + iov.iov_base = buf; + iov.iov_len = count; - return ret; -} + ret = glfs_preadv_async_common(glfd, &iov, 1, glfd->offset, flags, + _gf_false, fn, data); + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_pread_async34, glfs_pread_async, 3.4.0) int -glfs_read_async (struct glfs_fd *glfd, void *buf, size_t count, int flags, - glfs_io_cbk fn, void *data) +pub_glfs_pread_async34(struct glfs_fd *glfd, void *buf, size_t count, + off_t offset, int flags, glfs_io_cbk34 fn, void *data) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = buf; - iov.iov_len = count; + iov.iov_base = buf; + iov.iov_len = count; - ret = glfs_preadv_async (glfd, &iov, 1, glfd->offset, flags, fn, data); + ret = glfs_preadv_async_common(glfd, &iov, 1, offset, flags, _gf_true, + (void *)fn, data); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pread_async, 6.0) int -glfs_pread_async (struct glfs_fd *glfd, void *buf, size_t count, off_t offset, - int flags, glfs_io_cbk fn, void *data) +pub_glfs_pread_async(struct glfs_fd *glfd, void *buf, size_t count, + off_t offset, int flags, glfs_io_cbk fn, void *data) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = buf; - iov.iov_len = count; + iov.iov_base = buf; + iov.iov_len = count; - ret = glfs_preadv_async (glfd, &iov, 1, offset, flags, fn, data); + ret = glfs_preadv_async_common(glfd, &iov, 1, offset, flags, _gf_false, fn, + data); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC(glfs_readv_async34, glfs_readv_async, 3.4.0) int -glfs_readv_async (struct glfs_fd *glfd, const struct iovec *iov, int count, - int flags, glfs_io_cbk fn, void *data) +pub_glfs_readv_async34(struct glfs_fd *glfd, const struct iovec *iov, int count, + int flags, glfs_io_cbk34 fn, void *data) { - ssize_t ret = 0; + ssize_t ret = 0; - ret = glfs_preadv_async (glfd, iov, count, glfd->offset, flags, - fn, data); - return ret; -} + if (glfd == NULL) { + errno = EBADF; + return -1; + } -///// writev ///// + ret = glfs_preadv_async_common(glfd, iov, count, glfd->offset, flags, + _gf_true, (void *)fn, data); + return ret; +} -ssize_t -glfs_pwritev (struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, - off_t offset, int flags) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readv_async, 6.0) +int +pub_glfs_readv_async(struct glfs_fd *glfd, const struct iovec *iov, int count, + int flags, glfs_io_cbk fn, void *data) { - xlator_t *subvol = NULL; - int ret = -1; - size_t size = -1; - struct iobref *iobref = NULL; - struct iobuf *iobuf = NULL; - struct iovec iov = {0, }; - fd_t *fd = NULL; - - __glfs_entry_fd (glfd); + ssize_t ret = 0; - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (glfd == NULL) { + errno = EBADF; + return -1; + } - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + ret = glfs_preadv_async_common(glfd, iov, count, glfd->offset, flags, + _gf_false, fn, data); + return ret; +} - size = iov_length (iovec, iovcnt); +static ssize_t +glfs_pwritev_common(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, + off_t offset, int flags, struct glfs_stat *prestat, + struct glfs_stat *poststat) +{ + xlator_t *subvol = NULL; + int ret = -1; + struct iobref *iobref = NULL; + struct iobuf *iobuf = NULL; + struct iovec iov = { + 0, + }; + fd_t *fd = NULL; + struct iatt preiatt = + { + 0, + }, + postiatt = { + 0, + }; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = iobuf_copy(subvol->ctx->iobuf_pool, iovec, iovcnt, &iobref, &iobuf, + &iov); + if (ret) + goto out; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_writev(subvol, fd, &iov, 1, offset, iobref, flags, &preiatt, + &postiatt, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0) { + if (prestat) + glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); + if (poststat) + glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); + } + + if (ret <= 0) + goto out; + + glfd->offset = (offset + iov.iov_len); +out: + if (iobuf) + iobuf_unref(iobuf); + if (iobref) + iobref_unref(iobref); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); + + glfs_subvol_done(glfd->fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} - iobuf = iobuf_get2 (subvol->ctx->iobuf_pool, size); - if (!iobuf) { - ret = -1; - errno = ENOMEM; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_copy_file_range, 6.0) +ssize_t +pub_glfs_copy_file_range(struct glfs_fd *glfd_in, off64_t *off_in, + struct glfs_fd *glfd_out, off64_t *off_out, size_t len, + unsigned int flags, struct glfs_stat *statbuf, + struct glfs_stat *prestat, struct glfs_stat *poststat) +{ + xlator_t *subvol = NULL; + int ret = -1; + fd_t *fd_in = NULL; + fd_t *fd_out = NULL; + struct iatt preiatt = + { + 0, + }, + iattbuf = + { + 0, + }, + postiatt = { + 0, + }; + dict_t *fop_attr = NULL; + off64_t pos_in; + off64_t pos_out; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd_in, invalid_fs); + __GLFS_ENTRY_VALIDATE_FD(glfd_out, invalid_fs); + + GF_REF_GET(glfd_in); + GF_REF_GET(glfd_out); + + if (glfd_in->fs != glfd_out->fs) { + ret = -1; + errno = EXDEV; + goto out; + } + + subvol = glfs_active_subvol(glfd_in->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd_in = glfs_resolve_fd(glfd_in->fs, subvol, glfd_in); + if (!fd_in) { + ret = -1; + errno = EBADFD; + goto out; + } + + fd_out = glfs_resolve_fd(glfd_out->fs, subvol, glfd_out); + if (!fd_out) { + ret = -1; + errno = EBADFD; + goto out; + } + + /* + * This is based on how the vfs layer in the kernel handles + * copy_file_range call. Upon receiving it follows the + * below method to consider the offset. + * if (off_in != NULL) + * use the value off_in to perform the op + * else if off_in == NULL + * use the current file offset position to perform the op + * + * For gfapi, glfd->offset is used. For a freshly opened + * fd, the offset is set to 0. + */ + if (off_in) + pos_in = *off_in; + else + pos_in = glfd_in->offset; + + if (off_out) + pos_out = *off_out; + else + pos_out = glfd_out->offset; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_copy_file_range(subvol, fd_in, pos_in, fd_out, pos_out, len, + flags, &iattbuf, &preiatt, &postiatt, fop_attr, + NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0) { + pos_in += ret; + pos_out += ret; + + if (off_in) + *off_in = pos_in; + if (off_out) + *off_out = pos_out; + + if (statbuf) + glfs_iatt_to_statx(glfd_in->fs, &iattbuf, statbuf); + if (prestat) + glfs_iatt_to_statx(glfd_in->fs, &preiatt, prestat); + if (poststat) + glfs_iatt_to_statx(glfd_in->fs, &postiatt, poststat); + } + + if (ret <= 0) + goto out; + + /* + * If *off_in is NULL, then there is no offset info that can + * obtained from the input argument. Hence follow below method. + * If *off_in is NULL, then + * glfd->offset = offset + ret; + * else + * do nothing. + * + * According to the man page of copy_file_range, if off_in is + * NULL, then the offset of the source file is advanced by + * the return value of the fop. The same applies to off_out as + * well. Otherwise, if *off_in is not NULL, then the offset + * is not advanced by the filesystem. The entity which sends + * the copy_file_range call is supposed to advance the offset + * value in its buffer (pointed to by *off_in or *off_out) + * by the return value of copy_file_range. + */ + if (!off_in) + glfd_in->offset += ret; + + if (!off_out) + glfd_out->offset += ret; - iobref = iobref_new (); - if (!iobref) { - iobuf_unref (iobuf); - errno = ENOMEM; - ret = -1; - goto out; - } +out: + if (fd_in) + fd_unref(fd_in); + if (fd_out) + fd_unref(fd_out); + if (glfd_in) + GF_REF_PUT(glfd_in); + if (glfd_out) + GF_REF_PUT(glfd_out); + if (fop_attr) + dict_unref(fop_attr); + + glfs_subvol_done(glfd_in->fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} - ret = iobref_add (iobref, iobuf); - if (ret) { - iobuf_unref (iobuf); - iobref_unref (iobref); - errno = ENOMEM; - ret = -1; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwritev, 3.4.0) +ssize_t +pub_glfs_pwritev(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, + off_t offset, int flags) +{ + return glfs_pwritev_common(glfd, iovec, iovcnt, offset, flags, NULL, NULL); +} - iov_unload (iobuf_ptr (iobuf), iovec, iovcnt); /* FIXME!!! */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_write, 3.4.0) +ssize_t +pub_glfs_write(struct glfs_fd *glfd, const void *buf, size_t count, int flags) +{ + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = iobuf_ptr (iobuf); - iov.iov_len = size; + if (glfd == NULL) { + errno = EBADF; + return -1; + } - ret = syncop_writev (subvol, fd, &iov, 1, offset, iobref, flags); + iov.iov_base = (void *)buf; + iov.iov_len = count; - iobuf_unref (iobuf); - iobref_unref (iobref); + ret = pub_glfs_pwritev(glfd, &iov, 1, glfd->offset, flags); - if (ret <= 0) - goto out; + return ret; +} - glfd->offset = (offset + size); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_writev, 3.4.0) +ssize_t +pub_glfs_writev(struct glfs_fd *glfd, const struct iovec *iov, int count, + int flags) +{ + ssize_t ret = 0; -out: - if (fd) - fd_unref (fd); + if (glfd == NULL) { + errno = EBADF; + return -1; + } - glfs_subvol_done (glfd->fs, subvol); + ret = pub_glfs_pwritev(glfd, iov, count, glfd->offset, flags); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC(glfs_pwrite34, glfs_pwrite, 3.4.0) ssize_t -glfs_write (struct glfs_fd *glfd, const void *buf, size_t count, int flags) +pub_glfs_pwrite34(struct glfs_fd *glfd, const void *buf, size_t count, + off_t offset, int flags) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = (void *) buf; - iov.iov_len = count; + iov.iov_base = (void *)buf; + iov.iov_len = count; - ret = glfs_pwritev (glfd, &iov, 1, glfd->offset, flags); + ret = pub_glfs_pwritev(glfd, &iov, 1, offset, flags); - return ret; + return ret; } - - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwrite, 6.0) ssize_t -glfs_writev (struct glfs_fd *glfd, const struct iovec *iov, int count, - int flags) +pub_glfs_pwrite(struct glfs_fd *glfd, const void *buf, size_t count, + off_t offset, int flags, struct glfs_stat *prestat, + struct glfs_stat *poststat) { - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - ret = glfs_pwritev (glfd, iov, count, glfd->offset, flags); + iov.iov_base = (void *)buf; + iov.iov_len = count; - return ret; + ret = glfs_pwritev_common(glfd, &iov, 1, offset, flags, prestat, poststat); + + return ret; } +extern glfs_t * +pub_glfs_from_glfd(glfs_fd_t *); -ssize_t -glfs_pwrite (struct glfs_fd *glfd, const void *buf, size_t count, off_t offset, - int flags) +static int +glfs_pwritev_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) { - struct iovec iov = {0, }; - ssize_t ret = 0; - - iov.iov_base = (void *) buf; - iov.iov_len = count; - - ret = glfs_pwritev (glfd, &iov, 1, offset, flags); + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, + postbuf); - return ret; + return 0; } +static int +glfs_pwritev_async_common(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, + gf_boolean_t oldcb, glfs_io_cbk fn, void *data) +{ + struct glfs_io *gio = NULL; + int ret = -1; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct iobref *iobref = NULL; + struct iobuf *iobuf = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + /* Need to take explicit ref so that the fd + * is not destroyed before the fop is complete + */ + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + errno = ENOMEM; + goto out; + } + + gio->op = GF_FOP_WRITE; + gio->glfd = glfd; + gio->offset = offset; + gio->flags = flags; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + gio->count = 1; + gio->iov = GF_CALLOC(gio->count, sizeof(*(gio->iov)), gf_common_mt_iovec); + if (!gio->iov) { + errno = ENOMEM; + goto out; + } + + ret = iobuf_copy(subvol->ctx->iobuf_pool, iovec, count, &iobref, &iobuf, + gio->iov); + if (ret) + goto out; + + frame = syncop_create_frame(THIS); + if (!frame) { + errno = ENOMEM; + ret = -1; + goto out; + } + + frame->local = gio; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + STACK_WIND_COOKIE(frame, glfs_pwritev_async_cbk, subvol, subvol, + subvol->fops->writev, fd, gio->iov, gio->count, offset, + flags, iobref, fop_attr); + + ret = 0; +out: + if (ret) { + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + GF_FREE(gio); + /* + * If there is any error condition check after the frame + * creation, we have to destroy the frame root. + */ + glfs_subvol_done(glfd->fs, subvol); + } + if (fop_attr) + dict_unref(fop_attr); + + if (iobuf) + iobuf_unref(iobuf); + if (iobref) + iobref_unref(iobref); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_pwritev_async34, glfs_pwritev_async, 3.4.0) int -glfs_pwritev_async (struct glfs_fd *glfd, const struct iovec *iovec, int count, - off_t offset, int flags, glfs_io_cbk fn, void *data) +pub_glfs_pwritev_async34(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, glfs_io_cbk34 fn, + void *data) { - struct glfs_io *gio = NULL; - int ret = 0; + return glfs_pwritev_async_common(glfd, iovec, count, offset, flags, + _gf_true, (void *)fn, data); +} - gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t); - if (!gio) { - errno = ENOMEM; - return -1; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwritev_async, 6.0) +int +pub_glfs_pwritev_async(struct glfs_fd *glfd, const struct iovec *iovec, + int count, off_t offset, int flags, glfs_io_cbk fn, + void *data) +{ + return glfs_pwritev_async_common(glfd, iovec, count, offset, flags, + _gf_false, fn, data); +} - gio->iov = iov_dup (iovec, count); - if (!gio->iov) { - GF_FREE (gio); - errno = ENOMEM; - return -1; - } +GFAPI_SYMVER_PUBLIC(glfs_write_async34, glfs_write_async, 3.4.0) +int +pub_glfs_write_async34(struct glfs_fd *glfd, const void *buf, size_t count, + int flags, glfs_io_cbk34 fn, void *data) +{ + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - gio->op = GF_FOP_WRITE; - gio->glfd = glfd; - gio->count = count; - gio->offset = offset; - gio->flags = flags; - gio->fn = fn; - gio->data = data; + if (glfd == NULL) { + errno = EBADF; + return -1; + } - ret = synctask_new (glfs_from_glfd (glfd)->ctx->env, - glfs_io_async_task, glfs_io_async_cbk, - NULL, gio); + iov.iov_base = (void *)buf; + iov.iov_len = count; - if (ret) { - GF_FREE (gio->iov); - GF_FREE (gio); - } + ret = glfs_pwritev_async_common(glfd, &iov, 1, glfd->offset, flags, + _gf_true, (void *)fn, data); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_write_async, 6.0) int -glfs_write_async (struct glfs_fd *glfd, const void *buf, size_t count, int flags, - glfs_io_cbk fn, void *data) +pub_glfs_write_async(struct glfs_fd *glfd, const void *buf, size_t count, + int flags, glfs_io_cbk fn, void *data) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = (void *) buf; - iov.iov_len = count; + if (glfd == NULL) { + errno = EBADF; + return -1; + } - ret = glfs_pwritev_async (glfd, &iov, 1, glfd->offset, flags, fn, data); + iov.iov_base = (void *)buf; + iov.iov_len = count; - return ret; -} + ret = glfs_pwritev_async_common(glfd, &iov, 1, glfd->offset, flags, + _gf_false, fn, data); + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_pwrite_async34, glfs_pwrite_async, 3.4.0) int -glfs_pwrite_async (struct glfs_fd *glfd, const void *buf, int count, - off_t offset, int flags, glfs_io_cbk fn, void *data) +pub_glfs_pwrite_async34(struct glfs_fd *glfd, const void *buf, int count, + off_t offset, int flags, glfs_io_cbk34 fn, void *data) { - struct iovec iov = {0, }; - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - iov.iov_base = (void *) buf; - iov.iov_len = count; + iov.iov_base = (void *)buf; + iov.iov_len = count; - ret = glfs_pwritev_async (glfd, &iov, 1, offset, flags, fn, data); + ret = glfs_pwritev_async_common(glfd, &iov, 1, offset, flags, _gf_true, + (void *)fn, data); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwrite_async, 6.0) int -glfs_writev_async (struct glfs_fd *glfd, const struct iovec *iov, int count, - int flags, glfs_io_cbk fn, void *data) +pub_glfs_pwrite_async(struct glfs_fd *glfd, const void *buf, int count, + off_t offset, int flags, glfs_io_cbk fn, void *data) { - ssize_t ret = 0; + struct iovec iov = { + 0, + }; + ssize_t ret = 0; - ret = glfs_pwritev_async (glfd, iov, count, glfd->offset, flags, - fn, data); - return ret; -} + iov.iov_base = (void *)buf; + iov.iov_len = count; + + ret = glfs_pwritev_async_common(glfd, &iov, 1, offset, flags, _gf_false, fn, + data); + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_writev_async34, glfs_writev_async, 3.4.0) int -glfs_fsync (struct glfs_fd *glfd) +pub_glfs_writev_async34(struct glfs_fd *glfd, const struct iovec *iov, + int count, int flags, glfs_io_cbk34 fn, void *data) { - int ret = -1; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + ssize_t ret = 0; - __glfs_entry_fd (glfd); + if (glfd == NULL) { + errno = EBADF; + return -1; + } - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } - - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + ret = glfs_pwritev_async_common(glfd, iov, count, glfd->offset, flags, + _gf_true, (void *)fn, data); + return ret; +} - ret = syncop_fsync (subvol, fd, 0); -out: - if (fd) - fd_unref (fd); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_writev_async, 6.0) +int +pub_glfs_writev_async(struct glfs_fd *glfd, const struct iovec *iov, int count, + int flags, glfs_io_cbk fn, void *data) +{ + ssize_t ret = 0; - glfs_subvol_done (glfd->fs, subvol); + if (glfd == NULL) { + errno = EBADF; + return -1; + } - return ret; + ret = glfs_pwritev_async_common(glfd, iov, count, glfd->offset, flags, + _gf_false, fn, data); + return ret; } - static int -glfs_fsync_async_common (struct glfs_fd *glfd, glfs_io_cbk fn, void *data, - int dataonly) +glfs_fsync_common(struct glfs_fd *glfd, struct glfs_stat *prestat, + struct glfs_stat *poststat) { - struct glfs_io *gio = NULL; - int ret = 0; - - gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t); - if (!gio) { - errno = ENOMEM; - return -1; - } - - gio->op = GF_FOP_FSYNC; - gio->glfd = glfd; - gio->flags = dataonly; - gio->fn = fn; - gio->data = data; - - ret = synctask_new (glfs_from_glfd (glfd)->ctx->env, - glfs_io_async_task, glfs_io_async_cbk, - NULL, gio); + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct iatt preiatt = + { + 0, + }, + postiatt = { + 0, + }; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_fsync(subvol, fd, 0, &preiatt, &postiatt, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0) { + if (prestat) + glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); + if (poststat) + glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); + } +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); - if (ret) { - GF_FREE (gio->iov); - GF_FREE (gio); - } + glfs_subvol_done(glfd->fs, subvol); - return ret; + __GLFS_EXIT_FS; +invalid_fs: + return ret; } - +GFAPI_SYMVER_PUBLIC(glfs_fsync34, glfs_fsync, 3.4.0) int -glfs_fsync_async (struct glfs_fd *glfd, glfs_io_cbk fn, void *data) +pub_glfs_fsync34(struct glfs_fd *glfd) { - return glfs_fsync_async_common (glfd, fn, data, 0); + return glfs_fsync_common(glfd, NULL, NULL); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsync, 6.0) int -glfs_fdatasync (struct glfs_fd *glfd) +pub_glfs_fsync(struct glfs_fd *glfd, struct glfs_stat *prestat, + struct glfs_stat *poststat) { - int ret = -1; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + return glfs_fsync_common(glfd, prestat, poststat); +} - __glfs_entry_fd (glfd); +static int +glfs_fsync_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, + postbuf); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + return 0; +} - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } +static int +glfs_fsync_async_common(struct glfs_fd *glfd, gf_boolean_t oldcb, + glfs_io_cbk fn, void *data, int dataonly) +{ + struct glfs_io *gio = NULL; + int ret = 0; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + + /* Need to take explicit ref so that the fd + * is not destroyed before the fop is complete + */ + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + frame = syncop_create_frame(THIS); + if (!frame) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + errno = ENOMEM; + ret = -1; + goto out; + } + + gio->op = GF_FOP_FSYNC; + gio->glfd = glfd; + gio->flags = dataonly; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + + frame->local = gio; + + STACK_WIND_COOKIE(frame, glfs_fsync_async_cbk, subvol, subvol, + subvol->fops->fsync, fd, dataonly, NULL); - ret = syncop_fsync (subvol, fd, 1); out: - if (fd) - fd_unref (fd); - - glfs_subvol_done (glfd->fs, subvol); - - return ret; + if (ret) { + if (fd) + fd_unref(fd); + GF_REF_PUT(glfd); + GF_FREE(gio); + if (frame) + STACK_DESTROY(frame->root); + glfs_subvol_done(glfd->fs, subvol); + } + + return ret; } - +GFAPI_SYMVER_PUBLIC(glfs_fsync_async34, glfs_fsync_async, 3.4.0) int -glfs_fdatasync_async (struct glfs_fd *glfd, glfs_io_cbk fn, void *data) +pub_glfs_fsync_async34(struct glfs_fd *glfd, glfs_io_cbk34 fn, void *data) { - return glfs_fsync_async_common (glfd, fn, data, 1); -} + int ret = -1; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + ret = glfs_fsync_async_common(glfd, _gf_true, (void *)fn, data, 0); + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsync_async, 6.0) int -glfs_ftruncate (struct glfs_fd *glfd, off_t offset) +pub_glfs_fsync_async(struct glfs_fd *glfd, glfs_io_cbk fn, void *data) { - int ret = -1; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + int ret = -1; - __glfs_entry_fd (glfd); + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + ret = glfs_fsync_async_common(glfd, _gf_false, fn, data, 0); - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + __GLFS_EXIT_FS; - ret = syncop_ftruncate (subvol, fd, offset); +invalid_fs: + return ret; +} + +static int +glfs_fdatasync_common(struct glfs_fd *glfd, struct glfs_stat *prestat, + struct glfs_stat *poststat) +{ + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct iatt preiatt = + { + 0, + }, + postiatt = { + 0, + }; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_fsync(subvol, fd, 1, &preiatt, &postiatt, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0) { + if (prestat) + glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); + if (poststat) + glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); + } out: - if (fd) - fd_unref (fd); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); + + glfs_subvol_done(glfd->fs, subvol); - glfs_subvol_done (glfd->fs, subvol); + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC(glfs_fdatasync34, glfs_fdatasync, 3.4.0) +int +pub_glfs_fdatasync34(struct glfs_fd *glfd) +{ + return glfs_fdatasync_common(glfd, NULL, NULL); +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fdatasync, 6.0) int -glfs_ftruncate_async (struct glfs_fd *glfd, off_t offset, - glfs_io_cbk fn, void *data) +pub_glfs_fdatasync(struct glfs_fd *glfd, struct glfs_stat *prestat, + struct glfs_stat *poststat) { - struct glfs_io *gio = NULL; - int ret = 0; + return glfs_fdatasync_common(glfd, prestat, poststat); +} - gio = GF_CALLOC (1, sizeof (*gio), glfs_mt_glfs_io_t); - if (!gio) { - errno = ENOMEM; - return -1; - } +GFAPI_SYMVER_PUBLIC(glfs_fdatasync_async34, glfs_fdatasync_async, 3.4.0) +int +pub_glfs_fdatasync_async34(struct glfs_fd *glfd, glfs_io_cbk34 fn, void *data) +{ + int ret = -1; - gio->op = GF_FOP_FTRUNCATE; - gio->glfd = glfd; - gio->offset = offset; - gio->fn = fn; - gio->data = data; + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - ret = synctask_new (glfs_from_glfd (glfd)->ctx->env, - glfs_io_async_task, glfs_io_async_cbk, - NULL, gio); + ret = glfs_fsync_async_common(glfd, _gf_true, (void *)fn, data, 1); - if (ret) { - GF_FREE (gio->iov); - GF_FREE (gio); - } + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fdatasync_async, 6.0) int -glfs_access (struct glfs *fs, const char *path, int mode) +pub_glfs_fdatasync_async(struct glfs_fd *glfd, glfs_io_cbk fn, void *data) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + int ret = -1; - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ret = glfs_fsync_async_common(glfd, _gf_false, fn, data, 1); - if (ret) - goto out; + __GLFS_EXIT_FS; - ret = syncop_access (subvol, &loc, mode); +invalid_fs: + return ret; +} - ESTALE_RETRY (ret, errno, reval, &loc, retry); +static int +glfs_ftruncate_common(struct glfs_fd *glfd, off_t offset, + struct glfs_stat *prestat, struct glfs_stat *poststat) +{ + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct iatt preiatt = + { + 0, + }, + postiatt = { + 0, + }; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_ftruncate(subvol, fd, offset, &preiatt, &postiatt, fop_attr, + NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret >= 0) { + if (prestat) + glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); + if (poststat) + glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); + } out: - loc_wipe (&loc); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(glfd->fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC(glfs_ftruncate34, glfs_ftruncate, 3.4.0) int -glfs_symlink (struct glfs *fs, const char *data, const char *path) +pub_glfs_ftruncate34(struct glfs_fd *glfd, off_t offset) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - uuid_t gfid; - dict_t *xattr_req = NULL; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + return glfs_ftruncate_common(glfd, offset, NULL, NULL); +} - xattr_req = dict_new (); - if (!xattr_req) { - ret = -1; - errno = ENOMEM; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_ftruncate, 6.0) +int +pub_glfs_ftruncate(struct glfs_fd *glfd, off_t offset, + struct glfs_stat *prestat, struct glfs_stat *poststat) +{ + return glfs_ftruncate_common(glfd, offset, prestat, poststat); +} - uuid_generate (gfid); - ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16); - if (ret) { - ret = -1; - errno = ENOMEM; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_truncate, 3.7.15) +int +pub_glfs_truncate(struct glfs *fs, const char *path, off_t length) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (loc.inode) { - errno = EEXIST; - ret = -1; - goto out; - } + if (ret) + goto out; - if (ret == -1 && errno != ENOENT) - /* Any other type of error is fatal */ - goto out; + ret = syncop_truncate(subvol, &loc, length, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - if (ret == -1 && errno == ENOENT && !loc.parent) - /* The parent directory or an ancestor even - higher does not exist - */ - goto out; + ESTALE_RETRY(ret, errno, reval, &loc, retry); +out: + loc_wipe(&loc); - /* ret == -1 && errno == ENOENT */ - loc.inode = inode_new (loc.parent->table); - if (!loc.inode) { - ret = -1; - errno = ENOMEM; - goto out; - } + glfs_subvol_done(fs, subvol); - ret = syncop_symlink (subvol, &loc, data, xattr_req, &iatt); + __GLFS_EXIT_FS; - ESTALE_RETRY (ret, errno, reval, &loc, retry); +invalid_fs: + return ret; +} - if (ret == 0) - ret = glfs_loc_link (&loc, &iatt); -out: - loc_wipe (&loc); +static int +glfs_ftruncate_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, + postbuf); - if (xattr_req) - dict_unref (xattr_req); + return 0; +} - glfs_subvol_done (fs, subvol); +static int +glfs_ftruncate_async_common(struct glfs_fd *glfd, off_t offset, + gf_boolean_t oldcb, glfs_io_cbk fn, void *data) +{ + struct glfs_io *gio = NULL; + int ret = -1; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + /* Need to take explicit ref so that the fd + * is not destroyed before the fop is complete + */ + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + frame = syncop_create_frame(THIS); + if (!frame) { + errno = ENOMEM; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + errno = ENOMEM; + goto out; + } + + gio->op = GF_FOP_FTRUNCATE; + gio->glfd = glfd; + gio->offset = offset; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + + frame->local = gio; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + STACK_WIND_COOKIE(frame, glfs_ftruncate_async_cbk, subvol, subvol, + subvol->fops->ftruncate, fd, offset, fop_attr); + + ret = 0; - return ret; +out: + if (ret) { + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + GF_FREE(gio); + if (frame) + STACK_DESTROY(frame->root); + glfs_subvol_done(glfd->fs, subvol); + } + if (fop_attr) + dict_unref(fop_attr); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } - +GFAPI_SYMVER_PUBLIC(glfs_ftruncate_async34, glfs_ftruncate_async, 3.4.0) int -glfs_readlink (struct glfs *fs, const char *path, char *buf, size_t bufsiz) +pub_glfs_ftruncate_async34(struct glfs_fd *glfd, off_t offset, glfs_io_cbk34 fn, + void *data) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + return glfs_ftruncate_async_common(glfd, offset, _gf_true, (void *)fn, + data); +} - __glfs_entry_fs (fs); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_ftruncate_async, 6.0) +int +pub_glfs_ftruncate_async(struct glfs_fd *glfd, off_t offset, glfs_io_cbk fn, + void *data) +{ + return glfs_ftruncate_async_common(glfd, offset, _gf_false, fn, data); +} - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_access, 3.4.0) +int +pub_glfs_access(struct glfs *fs, const char *path, int mode) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); - - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - if (ret) - goto out; + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (iatt.ia_type != IA_IFLNK) { - ret = -1; - errno = EINVAL; - goto out; - } + if (ret) + goto out; - ret = syncop_readlink (subvol, &loc, &buf, bufsiz); + ret = syncop_access(subvol, &loc, mode, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_symlink, 3.4.0) int -glfs_mknod (struct glfs *fs, const char *path, mode_t mode, dev_t dev) +pub_glfs_symlink(struct glfs *fs, const char *data, const char *path) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - uuid_t gfid; - dict_t *xattr_req = NULL; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } - - xattr_req = dict_new (); - if (!xattr_req) { - ret = -1; - errno = ENOMEM; - goto out; - } - - uuid_generate (gfid); - ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16); - if (ret) { - ret = -1; - errno = ENOMEM; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (loc.inode) { - errno = EEXIST; - ret = -1; - goto out; - } + if (loc.inode) { + errno = EEXIST; + ret = -1; + goto out; + } - if (ret == -1 && errno != ENOENT) - /* Any other type of error is fatal */ - goto out; + if (ret == -1 && errno != ENOENT) + /* Any other type of error is fatal */ + goto out; - if (ret == -1 && errno == ENOENT && !loc.parent) - /* The parent directory or an ancestor even - higher does not exist - */ - goto out; + if (ret == -1 && errno == ENOENT && !loc.parent) + /* The parent directory or an ancestor even + higher does not exist + */ + goto out; - /* ret == -1 && errno == ENOENT */ - loc.inode = inode_new (loc.parent->table); - if (!loc.inode) { - ret = -1; - errno = ENOMEM; - goto out; - } + /* ret == -1 && errno == ENOENT */ + loc.inode = inode_new(loc.parent->table); + if (!loc.inode) { + ret = -1; + errno = ENOMEM; + goto out; + } - ret = syncop_mknod (subvol, &loc, mode, dev, xattr_req, &iatt); + ret = syncop_symlink(subvol, &loc, data, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0) - ret = glfs_loc_link (&loc, &iatt); + if (ret == 0) + ret = glfs_loc_link(&loc, &iatt); out: - loc_wipe (&loc); + loc_wipe(&loc); - if (xattr_req) - dict_unref (xattr_req); + if (xattr_req) + dict_unref(xattr_req); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readlink, 3.4.0) int -glfs_mkdir (struct glfs *fs, const char *path, mode_t mode) +pub_glfs_readlink(struct glfs *fs, const char *path, char *buf, size_t bufsiz) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - uuid_t gfid; - dict_t *xattr_req = NULL; - int reval = 0; + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + char *linkval = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } +retry: + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); - __glfs_entry_fs (fs); + if (ret) + goto out; - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (iatt.ia_type != IA_IFLNK) { + ret = -1; + errno = EINVAL; + goto out; + } - xattr_req = dict_new (); - if (!xattr_req) { - ret = -1; - errno = ENOMEM; - goto out; - } + ret = syncop_readlink(subvol, &loc, &linkval, bufsiz, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret > 0) { + memcpy(buf, linkval, ret); + GF_FREE(linkval); + } + + ESTALE_RETRY(ret, errno, reval, &loc, retry); +out: + loc_wipe(&loc); - uuid_generate (gfid); - ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16); - if (ret) { - ret = -1; - errno = ENOMEM; - goto out; - } + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mknod, 3.4.0) +int +pub_glfs_mknod(struct glfs *fs, const char *path, mode_t mode, dev_t dev) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (loc.inode) { - errno = EEXIST; - ret = -1; - goto out; - } + if (loc.inode) { + errno = EEXIST; + ret = -1; + goto out; + } - if (ret == -1 && errno != ENOENT) - /* Any other type of error is fatal */ - goto out; + if (ret == -1 && errno != ENOENT) + /* Any other type of error is fatal */ + goto out; - if (ret == -1 && errno == ENOENT && !loc.parent) - /* The parent directory or an ancestor even - higher does not exist - */ - goto out; + if (ret == -1 && errno == ENOENT && !loc.parent) + /* The parent directory or an ancestor even + higher does not exist + */ + goto out; - /* ret == -1 && errno == ENOENT */ - loc.inode = inode_new (loc.parent->table); - if (!loc.inode) { - ret = -1; - errno = ENOMEM; - goto out; - } + /* ret == -1 && errno == ENOENT */ + loc.inode = inode_new(loc.parent->table); + if (!loc.inode) { + ret = -1; + errno = ENOMEM; + goto out; + } - ret = syncop_mkdir (subvol, &loc, mode, xattr_req, &iatt); + ret = syncop_mknod(subvol, &loc, mode, dev, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0) - ret = glfs_loc_link (&loc, &iatt); + if (ret == 0) + ret = glfs_loc_link(&loc, &iatt); out: - loc_wipe (&loc); + loc_wipe(&loc); - if (xattr_req) - dict_unref (xattr_req); + if (xattr_req) + dict_unref(xattr_req); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mkdir, 3.4.0) int -glfs_unlink (struct glfs *fs, const char *path) +pub_glfs_mkdir(struct glfs *fs, const char *path, mode_t mode) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } +retry: + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - __glfs_entry_fs (fs); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + if (loc.inode) { + errno = EEXIST; + ret = -1; + goto out; + } - ESTALE_RETRY (ret, errno, reval, &loc, retry); + if (ret == -1 && errno != ENOENT) + /* Any other type of error is fatal */ + goto out; - if (ret) - goto out; + if (ret == -1 && errno == ENOENT && !loc.parent) + /* The parent directory or an ancestor even + higher does not exist + */ + goto out; - if (iatt.ia_type == IA_IFDIR) { - ret = -1; - errno = EISDIR; - goto out; - } + /* ret == -1 && errno == ENOENT */ + loc.inode = inode_new(loc.parent->table); + if (!loc.inode) { + ret = -1; + errno = ENOMEM; + goto out; + } - ret = syncop_unlink (subvol, &loc); + ret = syncop_mkdir(subvol, &loc, mode, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0) - ret = glfs_loc_unlink (&loc); + if (ret == 0) + ret = glfs_loc_link(&loc, &iatt); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + if (xattr_req) + dict_unref(xattr_req); - return ret; -} + glfs_subvol_done(fs, subvol); + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_unlink, 3.4.0) int -glfs_rmdir (struct glfs *fs, const char *path) +pub_glfs_unlink(struct glfs *fs, const char *path) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - if (iatt.ia_type != IA_IFDIR) { - ret = -1; - errno = ENOTDIR; - goto out; - } + if (iatt.ia_type == IA_IFDIR) { + ret = -1; + errno = EISDIR; + goto out; + } - ret = syncop_rmdir (subvol, &loc); + /* TODO: Add leaseid */ + ret = syncop_unlink(subvol, &loc, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == 0) - ret = glfs_loc_unlink (&loc); + if (ret == 0) + ret = glfs_loc_unlink(&loc); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_rmdir, 3.4.0) int -glfs_rename (struct glfs *fs, const char *oldpath, const char *newpath) +pub_glfs_rmdir(struct glfs *fs, const char *path) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t oldloc = {0, }; - loc_t newloc = {0, }; - struct iatt oldiatt = {0, }; - struct iatt newiatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, oldpath, &oldloc, &oldiatt, reval); + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &oldloc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; -retrynew: - ret = glfs_lresolve (fs, subvol, newpath, &newloc, &newiatt, reval); - - ESTALE_RETRY (ret, errno, reval, &newloc, retrynew); - - if (ret && errno != ENOENT && newloc.parent) - goto out; + if (ret) + goto out; - if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { - /* Either both old and new must be dirs, or both must be - non-dirs. Else, fail. - */ - ret = -1; - errno = EISDIR; - goto out; - } + if (iatt.ia_type != IA_IFDIR) { + ret = -1; + errno = ENOTDIR; + goto out; + } - /* TODO: check if new or old is a prefix of the other, and fail EINVAL */ + ret = syncop_rmdir(subvol, &loc, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ret = syncop_rename (subvol, &oldloc, &newloc); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret == -1 && errno == ESTALE) { - if (reval < DEFAULT_REVAL_COUNT) { - reval++; - loc_wipe (&oldloc); - loc_wipe (&newloc); - goto retry; - } - } - - if (ret == 0) - inode_rename (oldloc.parent->table, oldloc.parent, oldloc.name, - newloc.parent, newloc.name, oldloc.inode, - &oldiatt); + if (ret == 0) + ret = glfs_loc_unlink(&loc); out: - loc_wipe (&oldloc); - loc_wipe (&newloc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_rename, 3.4.0) int -glfs_link (struct glfs *fs, const char *oldpath, const char *newpath) +pub_glfs_rename(struct glfs *fs, const char *oldpath, const char *newpath) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t oldloc = {0, }; - loc_t newloc = {0, }; - struct iatt oldiatt = {0, }; - struct iatt newiatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t oldloc = { + 0, + }; + loc_t newloc = { + 0, + }; + struct iatt oldiatt = { + 0, + }; + struct iatt newiatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - ret = glfs_lresolve (fs, subvol, oldpath, &oldloc, &oldiatt, reval); + ret = glfs_lresolve(fs, subvol, oldpath, &oldloc, &oldiatt, reval); - ESTALE_RETRY (ret, errno, reval, &oldloc, retry); + ESTALE_RETRY(ret, errno, reval, &oldloc, retry); - if (ret) - goto out; + if (ret) + goto out; retrynew: - ret = glfs_lresolve (fs, subvol, newpath, &newloc, &newiatt, reval); + ret = glfs_lresolve(fs, subvol, newpath, &newloc, &newiatt, reval); + + ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); + + if (ret && errno != ENOENT && newloc.parent) + goto out; + + if (newiatt.ia_type != IA_INVAL) { + if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { + /* Either both old and new must be dirs, + * or both must be non-dirs. Else, fail. + */ + ret = -1; + errno = EISDIR; + goto out; + } + } + + /* TODO: - check if new or old is a prefix of the other, and fail EINVAL + * - Add leaseid */ + + ret = syncop_rename(subvol, &oldloc, &newloc, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret == -1 && errno == ESTALE) { + if (reval < DEFAULT_REVAL_COUNT) { + reval++; + loc_wipe(&oldloc); + loc_wipe(&newloc); + goto retry; + } + } + + if (ret == 0) { + inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, + newloc.parent, newloc.name, oldloc.inode, &oldiatt); + + if (newloc.inode && !inode_has_dentry(newloc.inode)) + inode_forget(newloc.inode, 0); + } +out: + loc_wipe(&oldloc); + loc_wipe(&newloc); - ESTALE_RETRY (ret, errno, reval, &newloc, retrynew); + glfs_subvol_done(fs, subvol); - if (ret == 0) { - ret = -1; - errno = EEXIST; - goto out; - } + __GLFS_EXIT_FS; - if (oldiatt.ia_type == IA_IFDIR) { - ret = -1; - errno = EISDIR; - goto out; - } +invalid_fs: + return ret; +} - ret = syncop_link (subvol, &oldloc, &newloc); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_link, 3.4.0) +int +pub_glfs_link(struct glfs *fs, const char *oldpath, const char *newpath) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t oldloc = { + 0, + }; + loc_t newloc = { + 0, + }; + struct iatt oldiatt = { + 0, + }; + struct iatt newiatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } +retry: + ret = glfs_lresolve(fs, subvol, oldpath, &oldloc, &oldiatt, reval); - if (ret == -1 && errno == ESTALE) { - loc_wipe (&oldloc); - loc_wipe (&newloc); - if (reval--) - goto retry; - } + ESTALE_RETRY(ret, errno, reval, &oldloc, retry); - if (ret == 0) - ret = glfs_loc_link (&newloc, &oldiatt); + if (ret) + goto out; +retrynew: + ret = glfs_lresolve(fs, subvol, newpath, &newloc, &newiatt, reval); + + ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); + + if (ret == 0) { + ret = -1; + errno = EEXIST; + goto out; + } + + if (oldiatt.ia_type == IA_IFDIR) { + ret = -1; + errno = EISDIR; + goto out; + } + + /* Filling the inode of the hard link to be same as that of the + original file + */ + if (newloc.inode) { + inode_unref(newloc.inode); + newloc.inode = NULL; + } + newloc.inode = inode_ref(oldloc.inode); + + ret = syncop_link(subvol, &oldloc, &newloc, &newiatt, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret == -1 && errno == ESTALE) { + loc_wipe(&oldloc); + loc_wipe(&newloc); + if (reval--) + goto retry; + } + + if (ret == 0) + ret = glfs_loc_link(&newloc, &newiatt); out: - loc_wipe (&oldloc); - loc_wipe (&newloc); + loc_wipe(&oldloc); + loc_wipe(&newloc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_opendir, 3.4.0) struct glfs_fd * -glfs_opendir (struct glfs *fs, const char *path) +pub_glfs_opendir(struct glfs *fs, const char *path) { - int ret = -1; - struct glfs_fd *glfd = NULL; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + glfd = glfs_fd_new(fs); + if (!glfd) + goto out; + + INIT_LIST_HEAD(&glfd->entries); +retry: + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); + + if (ret) + goto out; + + if (!IA_ISDIR(iatt.ia_type)) { + ret = -1; + errno = ENOTDIR; + goto out; + } + + if (glfd->fd) { + /* Retry. Safe to touch glfd->fd as we + still have not glfs_fd_bind() yet. + */ + fd_unref(glfd->fd); + glfd->fd = NULL; + } + + glfd->fd = fd_create(loc.inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); +out: + loc_wipe(&loc); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (ret && glfd) { + GF_REF_PUT(glfd); + glfd = NULL; + } else if (glfd) { + glfd_set_state_bind(glfd); + } - glfd = glfs_fd_new (fs); - if (!glfd) - goto out; + glfs_subvol_done(fs, subvol); - INIT_LIST_HEAD (&glfd->entries); -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + __GLFS_EXIT_FS; - ESTALE_RETRY (ret, errno, reval, &loc, retry); +invalid_fs: + return glfd; +} - if (ret) - goto out; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_closedir, 3.4.0) +int +pub_glfs_closedir(struct glfs_fd *glfd) +{ + int ret = -1; - if (!IA_ISDIR (iatt.ia_type)) { - ret = -1; - errno = ENOTDIR; - goto out; - } + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - if (glfd->fd) { - /* Retry. Safe to touch glfd->fd as we - still have not glfs_fd_bind() yet. - */ - fd_unref (glfd->fd); - glfd->fd = NULL; - } + gf_dirent_free(list_entry(&glfd->entries, gf_dirent_t, list)); - glfd->fd = fd_create (loc.inode, getpid()); - if (!glfd->fd) { - ret = -1; - errno = ENOMEM; - goto out; - } + glfs_mark_glfd_for_deletion(glfd); - ret = syncop_opendir (subvol, &loc, glfd->fd); + __GLFS_EXIT_FS; - ESTALE_RETRY (ret, errno, reval, &loc, retry); -out: - loc_wipe (&loc); + ret = 0; - if (ret && glfd) { - glfs_fd_destroy (glfd); - glfd = NULL; - } else { - fd_bind (glfd->fd); - glfs_fd_bind (glfd); - } +invalid_fs: + return ret; +} - glfs_subvol_done (fs, subvol); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_telldir, 3.4.0) +long +pub_glfs_telldir(struct glfs_fd *fd) +{ + if (fd == NULL) { + errno = EBADF; + return -1; + } - return glfd; + return fd->offset; } - -int -glfs_closedir (struct glfs_fd *glfd) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_seekdir, 3.4.0) +void +pub_glfs_seekdir(struct glfs_fd *fd, long offset) { - __glfs_entry_fd (glfd); - - gf_dirent_free (list_entry (&glfd->entries, gf_dirent_t, list)); + gf_dirent_t *entry = NULL; + gf_dirent_t *tmp = NULL; + + if (fd == NULL) { + errno = EBADF; + return; + } + + if (fd->offset == offset) + return; + + fd->offset = offset; + fd->next = NULL; + + list_for_each_entry_safe(entry, tmp, &fd->entries, list) + { + if (entry->d_off != offset) + continue; + + if (&tmp->list != &fd->entries) { + /* found! */ + fd->next = tmp; + return; + } + } + /* could not find entry at requested offset in the cache. + next readdir_r() will result in glfd_entry_refresh() + */ +} - glfs_fd_destroy (glfd); +static int +glfs_discard_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preop_stbuf, struct iatt *postop_stbuf, + dict_t *xdata) +{ + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, preop_stbuf, + postop_stbuf); - return 0; + return 0; } - -long -glfs_telldir (struct glfs_fd *fd) +static int +glfs_discard_async_common(struct glfs_fd *glfd, off_t offset, size_t len, + gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { - return fd->offset; + struct glfs_io *gio = NULL; + int ret = -1; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + /* Need to take explicit ref so that the fd + * is not destroyed before the fop is complete + */ + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + frame = syncop_create_frame(THIS); + if (!frame) { + errno = ENOMEM; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + errno = ENOMEM; + goto out; + } + + gio->op = GF_FOP_DISCARD; + gio->glfd = glfd; + gio->offset = offset; + gio->count = len; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + + frame->local = gio; + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + STACK_WIND_COOKIE(frame, glfs_discard_async_cbk, subvol, subvol, + subvol->fops->discard, fd, offset, len, fop_attr); + + ret = 0; +out: + if (ret) { + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + GF_FREE(gio); + if (frame) + STACK_DESTROY(frame->root); + glfs_subvol_done(glfd->fs, subvol); + } + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC(glfs_discard_async35, glfs_discard_async, 3.5.0) +int +pub_glfs_discard_async35(struct glfs_fd *glfd, off_t offset, size_t len, + glfs_io_cbk34 fn, void *data) +{ + return glfs_discard_async_common(glfd, offset, len, _gf_true, (void *)fn, + data); +} -void -glfs_seekdir (struct glfs_fd *fd, long offset) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_discard_async, 6.0) +int +pub_glfs_discard_async(struct glfs_fd *glfd, off_t offset, size_t len, + glfs_io_cbk fn, void *data) { - gf_dirent_t *entry = NULL; - gf_dirent_t *tmp = NULL; + return glfs_discard_async_common(glfd, offset, len, _gf_false, fn, data); +} - if (fd->offset == offset) - return; +static int +glfs_zerofill_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preop_stbuf, struct iatt *postop_stbuf, + dict_t *xdata) +{ + glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, preop_stbuf, + postop_stbuf); - fd->offset = offset; - fd->next = NULL; + return 0; +} - list_for_each_entry_safe (entry, tmp, &fd->entries, list) { - if (entry->d_off != offset) - continue; +static int +glfs_zerofill_async_common(struct glfs_fd *glfd, off_t offset, off_t len, + gf_boolean_t oldcb, glfs_io_cbk fn, void *data) +{ + struct glfs_io *gio = NULL; + int ret = -1; + call_frame_t *frame = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + /* Need to take explicit ref so that the fd + * is not destroyed before the fop is complete + */ + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + frame = syncop_create_frame(THIS); + if (!frame) { + errno = ENOMEM; + goto out; + } + + gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); + if (!gio) { + errno = ENOMEM; + goto out; + } + + gio->op = GF_FOP_ZEROFILL; + gio->glfd = glfd; + gio->offset = offset; + gio->count = len; + gio->oldcb = oldcb; + gio->fn = fn; + gio->data = data; + + frame->local = gio; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + STACK_WIND_COOKIE(frame, glfs_zerofill_async_cbk, subvol, subvol, + subvol->fops->zerofill, fd, offset, len, fop_attr); + ret = 0; +out: + if (ret) { + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + GF_FREE(gio); + if (frame) + STACK_DESTROY(frame->root); + glfs_subvol_done(glfd->fs, subvol); + } + if (fop_attr) + dict_unref(fop_attr); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} - if (&tmp->list != &fd->entries) { - /* found! */ - fd->next = tmp; - return; - } - } - /* could not find entry at requested offset in the cache. - next readdir_r() will result in glfd_entry_refresh() - */ +GFAPI_SYMVER_PUBLIC(glfs_zerofill_async35, glfs_zerofill_async, 3.5.0) +int +pub_glfs_zerofill_async35(struct glfs_fd *glfd, off_t offset, off_t len, + glfs_io_cbk34 fn, void *data) +{ + return glfs_zerofill_async_common(glfd, offset, len, _gf_true, (void *)fn, + data); } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_zerofill_async, 6.0) +int +pub_glfs_zerofill_async(struct glfs_fd *glfd, off_t offset, off_t len, + glfs_io_cbk fn, void *data) +{ + return glfs_zerofill_async_common(glfd, offset, len, _gf_false, fn, data); +} void -gf_dirent_to_dirent (gf_dirent_t *gf_dirent, struct dirent *dirent) +gf_dirent_to_dirent(gf_dirent_t *gf_dirent, struct dirent *dirent) { - dirent->d_ino = gf_dirent->d_ino; + dirent->d_ino = gf_dirent->d_ino; #ifdef _DIRENT_HAVE_D_OFF - dirent->d_off = gf_dirent->d_off; + dirent->d_off = gf_dirent->d_off; #endif #ifdef _DIRENT_HAVE_D_TYPE - dirent->d_type = gf_dirent->d_type; + dirent->d_type = gf_dirent->d_type; #endif #ifdef _DIRENT_HAVE_D_NAMLEN - dirent->d_namlen = strlen (gf_dirent->d_name); + dirent->d_namlen = strlen(gf_dirent->d_name); #endif - strncpy (dirent->d_name, gf_dirent->d_name, 256); + snprintf(dirent->d_name, NAME_MAX + 1, "%s", gf_dirent->d_name); } - int -glfd_entry_refresh (struct glfs_fd *glfd, int plus) +glfd_entry_refresh(struct glfs_fd *glfd, int plus) { - xlator_t *subvol = NULL; - gf_dirent_t entries; - gf_dirent_t old; - int ret = -1; - fd_t *fd = NULL; - - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } - - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } - - if (fd->inode->ia_type != IA_IFDIR) { - ret = -1; - errno = EBADF; - goto out; - } - - INIT_LIST_HEAD (&entries.list); - INIT_LIST_HEAD (&old.list); - - if (plus) - ret = syncop_readdirp (subvol, fd, 131072, glfd->offset, - NULL, &entries); - else - ret = syncop_readdir (subvol, fd, 131072, glfd->offset, - &entries); - if (ret >= 0) { - if (plus) - gf_link_inodes_from_dirent (THIS, fd->inode, &entries); - - list_splice_init (&glfd->entries, &old.list); - list_splice_init (&entries.list, &glfd->entries); - - /* spurious errno is dangerous for glfd_entry_next() */ - errno = 0; - } - - if (ret > 0) - glfd->next = list_entry (glfd->entries.next, gf_dirent_t, list); - - gf_dirent_free (&old); + xlator_t *subvol = NULL; + gf_dirent_t entries; + gf_dirent_t old; + gf_dirent_t *entry = NULL; + int ret = -1; + fd_t *fd = NULL; + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + if (fd->inode->ia_type != IA_IFDIR) { + ret = -1; + errno = EBADF; + goto out; + } + + INIT_LIST_HEAD(&entries.list); + INIT_LIST_HEAD(&old.list); + + if (plus) + ret = syncop_readdirp(subvol, fd, 131072, glfd->offset, &entries, NULL, + NULL); + else + ret = syncop_readdir(subvol, fd, 131072, glfd->offset, &entries, NULL, + NULL); + DECODE_SYNCOP_ERR(ret); + if (ret >= 0) { + if (plus) { + list_for_each_entry(entry, &entries.list, list) + { + if ((!entry->inode && (!IA_ISDIR(entry->d_stat.ia_type))) || + ((entry->d_stat.ia_ctime == 0) && + strcmp(entry->d_name, ".") && + strcmp(entry->d_name, ".."))) { + /* entry->inode for directories will be + * always set to null to force a lookup + * on the dentry. Hence to not degrade + * readdir performance, we skip lookups + * for directory entries. Also we will have + * proper stat if directory present on + * hashed subvolume. + * + * In addition, if the stat is invalid, force + * lookup to fetch proper stat. + */ + gf_fill_iatt_for_dirent(entry, fd->inode, subvol); + } + } + + gf_link_inodes_from_dirent(THIS, fd->inode, &entries); + } + + list_splice_init(&glfd->entries, &old.list); + list_splice_init(&entries.list, &glfd->entries); + + /* spurious errno is dangerous for glfd_entry_next() */ + errno = 0; + } + + if (ret > 0) + glfd->next = list_entry(glfd->entries.next, gf_dirent_t, list); + + gf_dirent_free(&old); out: - if (fd) - fd_unref (fd); + if (fd) + fd_unref(fd); - glfs_subvol_done (glfd->fs, subvol); + glfs_subvol_done(glfd->fs, subvol); - return ret; + return ret; } - gf_dirent_t * -glfd_entry_next (struct glfs_fd *glfd, int plus) +glfd_entry_next(struct glfs_fd *glfd, int plus) { - gf_dirent_t *entry = NULL; - int ret = -1; + gf_dirent_t *entry = NULL; + int ret = -1; - if (!glfd->offset || !glfd->next) { - ret = glfd_entry_refresh (glfd, plus); - if (ret < 0) - return NULL; - } + if (!glfd->offset || !glfd->next) { + ret = glfd_entry_refresh(glfd, plus); + if (ret < 0) + return NULL; + } - entry = glfd->next; - if (!entry) - return NULL; + entry = glfd->next; + if (!entry) + return NULL; - if (&entry->next->list == &glfd->entries) - glfd->next = NULL; - else - glfd->next = entry->next; + if (&entry->next->list == &glfd->entries) + glfd->next = NULL; + else + glfd->next = entry->next; - glfd->offset = entry->d_off; + glfd->offset = entry->d_off; - return entry; + return entry; } +struct dirent * +glfs_readdirbuf_get(struct glfs_fd *glfd) +{ + struct dirent *buf = NULL; + + LOCK(&glfd->fd->lock); + { + buf = glfd->readdirbuf; + if (buf) { + memset(buf, 0, READDIRBUF_SIZE); + goto unlock; + } + + buf = GF_CALLOC(1, READDIRBUF_SIZE, glfs_mt_readdirbuf_t); + if (!buf) { + errno = ENOMEM; + goto unlock; + } + + glfd->readdirbuf = buf; + } +unlock: + UNLOCK(&glfd->fd->lock); + + return buf; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdirplus_r, 3.4.0) int -glfs_readdirplus_r (struct glfs_fd *glfd, struct stat *stat, struct dirent *buf, - struct dirent **res) +pub_glfs_readdirplus_r(struct glfs_fd *glfd, struct stat *stat, + struct dirent *ext, struct dirent **res) { - int ret = 0; - gf_dirent_t *entry = NULL; + int ret = 0; + gf_dirent_t *entry = NULL; + struct dirent *buf = NULL; - __glfs_entry_fd (glfd); + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - errno = 0; - entry = glfd_entry_next (glfd, !!stat); - if (errno) - ret = -1; + GF_REF_GET(glfd); - if (res) { - if (entry) - *res = buf; - else - *res = NULL; - } + errno = 0; - if (entry) { - gf_dirent_to_dirent (entry, buf); - if (stat) - glfs_iatt_to_stat (glfd->fs, &entry->d_stat, stat); - } + if (ext) + buf = ext; + else + buf = glfs_readdirbuf_get(glfd); - return ret; -} + if (!buf) { + errno = ENOMEM; + ret = -1; + goto out; + } + entry = glfd_entry_next(glfd, !!stat); + if (errno) + ret = -1; -int -glfs_readdir_r (struct glfs_fd *glfd, struct dirent *buf, struct dirent **res) -{ - return glfs_readdirplus_r (glfd, 0, buf, res); -} + if (res) { + if (entry) + *res = buf; + else + *res = NULL; + } + if (entry) { + gf_dirent_to_dirent(entry, buf); + if (stat) + glfs_iatt_to_stat(glfd->fs, &entry->d_stat, stat); + } -int -glfs_statvfs (struct glfs *fs, const char *path, struct statvfs *buf) -{ - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); +out: + if (glfd) + GF_REF_PUT(glfd); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + __GLFS_EXIT_FS; - ESTALE_RETRY (ret, errno, reval, &loc, retry); + return ret; - if (ret) - goto out; +invalid_fs: + return -1; +} - ret = syncop_statfs (subvol, &loc, buf); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdir_r, 3.4.0) +int +pub_glfs_readdir_r(struct glfs_fd *glfd, struct dirent *buf, + struct dirent **res) +{ + return pub_glfs_readdirplus_r(glfd, 0, buf, res); +} - ESTALE_RETRY (ret, errno, reval, &loc, retry); -out: - loc_wipe (&loc); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdirplus, 3.5.0) +struct dirent * +pub_glfs_readdirplus(struct glfs_fd *glfd, struct stat *stat) +{ + struct dirent *res = NULL; + int ret = -1; - glfs_subvol_done (fs, subvol); + ret = pub_glfs_readdirplus_r(glfd, stat, NULL, &res); + if (ret) + return NULL; - return ret; + return res; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdir, 3.5.0) +struct dirent * +pub_glfs_readdir(struct glfs_fd *glfd) +{ + return pub_glfs_readdirplus(glfd, NULL); +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_statvfs, 3.4.0) int -glfs_setattr (struct glfs *fs, const char *path, struct iatt *iatt, - int valid, int follow) +pub_glfs_statvfs(struct glfs *fs, const char *path, struct statvfs *buf) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt riatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - if (follow) - ret = glfs_resolve (fs, subvol, path, &loc, &riatt, reval); - else - ret = glfs_lresolve (fs, subvol, path, &loc, &riatt, reval); + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = syncop_setattr (subvol, &loc, iatt, valid, 0, 0); + ret = syncop_statfs(subvol, &loc, buf, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setattr, 6.0) int -glfs_fsetattr (struct glfs_fd *glfd, struct iatt *iatt, int valid) +pub_glfs_setattr(struct glfs *fs, const char *path, struct glfs_stat *stat, + int follow) { - int ret = -1; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + int ret = -1; + int glvalid; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt riatt = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + GF_VALIDATE_OR_GOTO("glfs_setattr", stat, out); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } +retry: + if (follow) + ret = glfs_resolve(fs, subvol, path, &loc, &riatt, reval); + else + ret = glfs_lresolve(fs, subvol, path, &loc, &riatt, reval); - __glfs_entry_fd (glfd); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (ret) + goto out; - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + glfs_iatt_from_statx(&iatt, stat); + glfsflags_from_gfapiflags(stat, &glvalid); - ret = syncop_fsetattr (subvol, fd, iatt, valid, 0, 0); + /* TODO : Add leaseid */ + ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + ESTALE_RETRY(ret, errno, reval, &loc, retry); out: - if (fd) - fd_unref (fd); + loc_wipe(&loc); - glfs_subvol_done (glfd->fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsetattr, 6.0) int -glfs_chmod (struct glfs *fs, const char *path, mode_t mode) +pub_glfs_fsetattr(struct glfs_fd *glfd, struct glfs_stat *stat) { - int ret = -1; - struct iatt iatt = {0, }; - int valid = 0; + int ret = -1; + int glvalid; + struct iatt iatt = { + 0, + }; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + GF_VALIDATE_OR_GOTO("glfs_fsetattr", stat, out); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + glfs_iatt_from_statx(&iatt, stat); + glfsflags_from_gfapiflags(stat, &glvalid); + + /* TODO : Add leaseid */ + ret = syncop_fsetattr(subvol, fd, &iatt, glvalid, 0, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); - iatt.ia_prot = ia_prot_from_st_mode (mode); - valid = GF_SET_ATTR_MODE; + glfs_subvol_done(glfd->fs, subvol); - ret = glfs_setattr (fs, path, &iatt, valid, 1); + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chmod, 3.4.0) int -glfs_fchmod (struct glfs_fd *glfd, mode_t mode) +pub_glfs_chmod(struct glfs *fs, const char *path, mode_t mode) { - int ret = -1; - struct iatt iatt = {0, }; - int valid = 0; + int ret = -1; + struct glfs_stat stat = { + 0, + }; - iatt.ia_prot = ia_prot_from_st_mode (mode); - valid = GF_SET_ATTR_MODE; + stat.glfs_st_mode = mode; + stat.glfs_st_mask = GLFS_STAT_MODE; - ret = glfs_fsetattr (glfd, &iatt, valid); + ret = glfs_setattr(fs, path, &stat, 1); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchmod, 3.4.0) int -glfs_chown (struct glfs *fs, const char *path, uid_t uid, gid_t gid) +pub_glfs_fchmod(struct glfs_fd *glfd, mode_t mode) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = -1; + struct glfs_stat stat = { + 0, + }; - iatt.ia_uid = uid; - iatt.ia_gid = gid; - valid = GF_SET_ATTR_UID|GF_SET_ATTR_GID; + stat.glfs_st_mode = mode; + stat.glfs_st_mask = GLFS_STAT_MODE; - ret = glfs_setattr (fs, path, &iatt, valid, 1); + ret = glfs_fsetattr(glfd, &stat); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chown, 3.4.0) int -glfs_lchown (struct glfs *fs, const char *path, uid_t uid, gid_t gid) +pub_glfs_chown(struct glfs *fs, const char *path, uid_t uid, gid_t gid) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = 0; + struct glfs_stat stat = { + 0, + }; - iatt.ia_uid = uid; - iatt.ia_gid = gid; - valid = GF_SET_ATTR_UID|GF_SET_ATTR_GID; + if (uid != (uid_t)-1) { + stat.glfs_st_uid = uid; + stat.glfs_st_mask = GLFS_STAT_UID; + } - ret = glfs_setattr (fs, path, &iatt, valid, 0); + if (gid != (uid_t)-1) { + stat.glfs_st_gid = gid; + stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; + } - return ret; -} + if (stat.glfs_st_mask) + ret = glfs_setattr(fs, path, &stat, 1); + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lchown, 3.4.0) int -glfs_fchown (struct glfs_fd *glfd, uid_t uid, gid_t gid) +pub_glfs_lchown(struct glfs *fs, const char *path, uid_t uid, gid_t gid) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = 0; + struct glfs_stat stat = { + 0, + }; - iatt.ia_uid = uid; - iatt.ia_gid = gid; - valid = GF_SET_ATTR_UID|GF_SET_ATTR_GID; + if (uid != (uid_t)-1) { + stat.glfs_st_uid = uid; + stat.glfs_st_mask = GLFS_STAT_UID; + } - ret = glfs_fsetattr (glfd, &iatt, valid); + if (gid != (uid_t)-1) { + stat.glfs_st_gid = gid; + stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; + } - return ret; -} + if (stat.glfs_st_mask) + ret = glfs_setattr(fs, path, &stat, 0); + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchown, 3.4.0) int -glfs_utimens (struct glfs *fs, const char *path, struct timespec times[2]) +pub_glfs_fchown(struct glfs_fd *glfd, uid_t uid, gid_t gid) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = 0; + struct glfs_stat stat = { + 0, + }; - iatt.ia_atime = times[0].tv_sec; - iatt.ia_atime_nsec = times[0].tv_nsec; - iatt.ia_mtime = times[1].tv_sec; - iatt.ia_mtime_nsec = times[1].tv_nsec; + if (uid != (uid_t)-1) { + stat.glfs_st_uid = uid; + stat.glfs_st_mask = GLFS_STAT_UID; + } - valid = GF_SET_ATTR_ATIME|GF_SET_ATTR_MTIME; + if (gid != (uid_t)-1) { + stat.glfs_st_gid = gid; + stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; + } - ret = glfs_setattr (fs, path, &iatt, valid, 1); + if (stat.glfs_st_mask) + ret = glfs_fsetattr(glfd, &stat); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_utimens, 3.4.0) int -glfs_lutimens (struct glfs *fs, const char *path, struct timespec times[2]) +pub_glfs_utimens(struct glfs *fs, const char *path, + const struct timespec times[2]) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = -1; + struct glfs_stat stat = { + 0, + }; - iatt.ia_atime = times[0].tv_sec; - iatt.ia_atime_nsec = times[0].tv_nsec; - iatt.ia_mtime = times[1].tv_sec; - iatt.ia_mtime_nsec = times[1].tv_nsec; + stat.glfs_st_atime = times[0]; + stat.glfs_st_mtime = times[1]; - valid = GF_SET_ATTR_ATIME|GF_SET_ATTR_MTIME; + stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; - ret = glfs_setattr (fs, path, &iatt, valid, 0); + ret = glfs_setattr(fs, path, &stat, 1); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lutimens, 3.4.0) int -glfs_futimens (struct glfs_fd *glfd, struct timespec times[2]) +pub_glfs_lutimens(struct glfs *fs, const char *path, + const struct timespec times[2]) { - int ret = -1; - int valid = 0; - struct iatt iatt = {0, }; + int ret = -1; + struct glfs_stat stat = { + 0, + }; - iatt.ia_atime = times[0].tv_sec; - iatt.ia_atime_nsec = times[0].tv_nsec; - iatt.ia_mtime = times[1].tv_sec; - iatt.ia_mtime_nsec = times[1].tv_nsec; + stat.glfs_st_atime = times[0]; + stat.glfs_st_mtime = times[1]; - valid = GF_SET_ATTR_ATIME|GF_SET_ATTR_MTIME; + stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; - ret = glfs_fsetattr (glfd, &iatt, valid); + ret = glfs_setattr(fs, path, &stat, 0); - return ret; + return ret; } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_futimens, 3.4.0) int -glfs_getxattr_process (void *value, size_t size, dict_t *xattr, - const char *name) +pub_glfs_futimens(struct glfs_fd *glfd, const struct timespec times[2]) { - data_t *data = NULL; - int ret = -1; + int ret = -1; + struct glfs_stat stat = { + 0, + }; - data = dict_get (xattr, (char *)name); - if (!data) { - errno = ENODATA; - ret = -1; - goto out; - } + stat.glfs_st_atime = times[0]; + stat.glfs_st_mtime = times[1]; - ret = data->len; - if (!value || !size) - goto out; + stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; - if (size < ret) { - ret = -1; - errno = ERANGE; - goto out; - } + ret = glfs_fsetattr(glfd, &stat); - memcpy (value, data->data, ret); -out: - if (xattr) - dict_unref (xattr); - return ret; + return ret; } +int +glfs_getxattr_process(void *value, size_t size, dict_t *xattr, const char *name) +{ + data_t *data = NULL; + int ret = -1; + + data = dict_get(xattr, (char *)name); + if (!data) { + errno = ENODATA; + ret = -1; + goto out; + } + + ret = data->len; + if (!value || !size) + goto out; + + if (size < ret) { + ret = -1; + errno = ERANGE; + goto out; + } + + memcpy(value, data->data, ret); +out: + return ret; +} ssize_t -glfs_getxattr_common (struct glfs *fs, const char *path, const char *name, - void *value, size_t size, int follow) -{ - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - dict_t *xattr = NULL; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } +glfs_getxattr_common(struct glfs *fs, const char *path, const char *name, + void *value, size_t size, int follow) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + dict_t *xattr = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (!name || *name == '\0') { + ret = -1; + errno = EINVAL; + goto out; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + ret = -1; + errno = ENAMETOOLONG; + goto out; + } + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + retry: - if (follow) - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); - else - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + if (follow) + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + else + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = syncop_getxattr (subvol, &loc, &xattr, name); + ret = syncop_getxattr(subvol, &loc, &xattr, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = glfs_getxattr_process (value, size, xattr, name); + ret = glfs_getxattr_process(value, size, xattr, name); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + if (xattr) + dict_unref(xattr); - return ret; -} + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_getxattr, 3.4.0) ssize_t -glfs_getxattr (struct glfs *fs, const char *path, const char *name, - void *value, size_t size) +pub_glfs_getxattr(struct glfs *fs, const char *path, const char *name, + void *value, size_t size) { - return glfs_getxattr_common (fs, path, name, value, size, 1); + return glfs_getxattr_common(fs, path, name, value, size, 1); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lgetxattr, 3.4.0) ssize_t -glfs_lgetxattr (struct glfs *fs, const char *path, const char *name, - void *value, size_t size) +pub_glfs_lgetxattr(struct glfs *fs, const char *path, const char *name, + void *value, size_t size) { - return glfs_getxattr_common (fs, path, name, value, size, 0); + return glfs_getxattr_common(fs, path, name, value, size, 0); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fgetxattr, 3.4.0) ssize_t -glfs_fgetxattr (struct glfs_fd *glfd, const char *name, void *value, - size_t size) +pub_glfs_fgetxattr(struct glfs_fd *glfd, const char *name, void *value, + size_t size) { - int ret = -1; - xlator_t *subvol = NULL; - dict_t *xattr = NULL; - fd_t *fd = NULL; - - __glfs_entry_fd (glfd); - - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } - - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } - - ret = syncop_fgetxattr (subvol, fd, &xattr, name); - if (ret) - goto out; - - ret = glfs_getxattr_process (value, size, xattr, name); + int ret = -1; + xlator_t *subvol = NULL; + dict_t *xattr = NULL; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + if (!name || *name == '\0') { + ret = -1; + errno = EINVAL; + goto out; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + ret = -1; + errno = ENAMETOOLONG; + goto out; + } + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = syncop_fgetxattr(subvol, fd, &xattr, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret) + goto out; + + ret = glfs_getxattr_process(value, size, xattr, name); out: - if (fd) - fd_unref (fd); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (xattr) + dict_unref(xattr); - glfs_subvol_done (glfd->fs, subvol); + glfs_subvol_done(glfd->fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} int -glfs_listxattr_process (void *value, size_t size, dict_t *xattr) +glfs_listxattr_process(void *value, size_t size, dict_t *xattr) { - int ret = -1; + int ret = -1; - ret = dict_keys_join (NULL, 0, xattr, NULL); + if (!xattr) + goto out; - if (!value || !size) - goto out; + ret = dict_keys_join(NULL, 0, xattr, NULL); - if (size < ret) { - ret = -1; - errno = ERANGE; - goto out; - } + if (!value || !size) + goto out; + + if (size < ret) { + ret = -1; + errno = ERANGE; + } else { + dict_keys_join(value, size, xattr, NULL); + } - dict_keys_join (value, size, xattr, NULL); out: - if (xattr) - dict_unref (xattr); - return ret; + return ret; } - ssize_t -glfs_listxattr_common (struct glfs *fs, const char *path, void *value, - size_t size, int follow) +glfs_listxattr_common(struct glfs *fs, const char *path, void *value, + size_t size, int follow) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - dict_t *xattr = NULL; - int reval = 0; - - __glfs_entry_fs (fs); - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + dict_t *xattr = NULL; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - if (follow) - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); - else - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + if (follow) + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + else + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = syncop_getxattr (subvol, &loc, &xattr, NULL); + ret = syncop_getxattr(subvol, &loc, &xattr, NULL, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = glfs_listxattr_process (value, size, xattr); + ret = glfs_listxattr_process(value, size, xattr); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + if (xattr) + dict_unref(xattr); - return ret; -} + glfs_subvol_done(fs, subvol); + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_listxattr, 3.4.0) ssize_t -glfs_listxattr (struct glfs *fs, const char *path, void *value, size_t size) +pub_glfs_listxattr(struct glfs *fs, const char *path, void *value, size_t size) { - return glfs_listxattr_common (fs, path, value, size, 1); + return glfs_listxattr_common(fs, path, value, size, 1); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_llistxattr, 3.4.0) ssize_t -glfs_llistxattr (struct glfs *fs, const char *path, void *value, size_t size) +pub_glfs_llistxattr(struct glfs *fs, const char *path, void *value, size_t size) { - return glfs_listxattr_common (fs, path, value, size, 0); + return glfs_listxattr_common(fs, path, value, size, 0); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_flistxattr, 3.4.0) ssize_t -glfs_flistxattr (struct glfs_fd *glfd, void *value, size_t size) +pub_glfs_flistxattr(struct glfs_fd *glfd, void *value, size_t size) { - int ret = -1; - xlator_t *subvol = NULL; - dict_t *xattr = NULL; - fd_t *fd = NULL; + int ret = -1; + xlator_t *subvol = NULL; + dict_t *xattr = NULL; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = syncop_fgetxattr(subvol, fd, &xattr, NULL, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret) + goto out; + + ret = glfs_listxattr_process(value, size, xattr); +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (xattr) + dict_unref(xattr); - __glfs_entry_fd (glfd); + glfs_subvol_done(glfd->fs, subvol); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + __GLFS_EXIT_FS; - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } +invalid_fs: + return ret; +} - ret = syncop_fgetxattr (subvol, fd, &xattr, NULL); - if (ret) - goto out; +int +glfs_setxattr_common(struct glfs *fs, const char *path, const char *name, + const void *value, size_t size, int flags, int follow) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + dict_t *xattr = NULL; + int reval = 0; + void *value_cp = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (!name || *name == '\0') { + ret = -1; + errno = EINVAL; + goto out; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + ret = -1; + errno = ENAMETOOLONG; + goto out; + } + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } - ret = glfs_listxattr_process (value, size, xattr); -out: - if (fd) - fd_unref (fd); +retry: + if (follow) + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + else + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - glfs_subvol_done (glfd->fs, subvol); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - return ret; -} + if (ret) + goto out; + value_cp = gf_memdup(value, size); + GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, + "Failed to" + " duplicate setxattr value", + out); -dict_t * -dict_for_key_value (const char *name, const char *value, size_t size) -{ - dict_t *xattr = NULL; - int ret = 0; + xattr = dict_for_key_value(name, value_cp, size, _gf_false); + if (!xattr) { + GF_FREE(value_cp); + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = syncop_setxattr(subvol, &loc, xattr, flags, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - xattr = dict_new (); - if (!xattr) - return NULL; +out: + loc_wipe(&loc); + if (xattr) + dict_unref(xattr); - ret = dict_set_static_bin (xattr, (char *)name, (void *)value, size); - if (ret) { - dict_destroy (xattr); - xattr = NULL; - } + glfs_subvol_done(fs, subvol); - return xattr; + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setxattr, 3.4.0) +int +pub_glfs_setxattr(struct glfs *fs, const char *path, const char *name, + const void *value, size_t size, int flags) +{ + return glfs_setxattr_common(fs, path, name, value, size, flags, 1); +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lsetxattr, 3.4.0) int -glfs_setxattr_common (struct glfs *fs, const char *path, const char *name, - const void *value, size_t size, int flags, int follow) +pub_glfs_lsetxattr(struct glfs *fs, const char *path, const char *name, + const void *value, size_t size, int flags) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - dict_t *xattr = NULL; - int reval = 0; + return glfs_setxattr_common(fs, path, name, value, size, flags, 0); +} - __glfs_entry_fs (fs); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsetxattr, 3.4.0) +int +pub_glfs_fsetxattr(struct glfs_fd *glfd, const char *name, const void *value, + size_t size, int flags) +{ + int ret = -1; + xlator_t *subvol = NULL; + dict_t *xattr = NULL; + fd_t *fd = NULL; + void *value_cp = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + if (!name || *name == '\0') { + ret = -1; + errno = EINVAL; + goto out; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + ret = -1; + errno = ENAMETOOLONG; + goto out; + } + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + value_cp = gf_memdup(value, size); + GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, + "Failed to" + " duplicate setxattr value", + out); + + xattr = dict_for_key_value(name, value_cp, size, _gf_false); + if (!xattr) { + GF_FREE(value_cp); + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = syncop_fsetxattr(subvol, fd, xattr, flags, NULL, NULL); + DECODE_SYNCOP_ERR(ret); +out: + if (xattr) + dict_unref(xattr); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - if (follow) - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); - else - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + + glfs_subvol_done(glfd->fs, subvol); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +int +glfs_removexattr_common(struct glfs *fs, const char *path, const char *name, + int follow) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } +retry: + if (follow) + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); + else + ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); - if (ret) - goto out; + ESTALE_RETRY(ret, errno, reval, &loc, retry); - xattr = dict_for_key_value (name, value, size); - if (!xattr) { - ret = -1; - errno = ENOMEM; - goto out; - } + if (ret) + goto out; - ret = syncop_setxattr (subvol, &loc, xattr, flags); + ret = syncop_removexattr(subvol, &loc, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); out: - loc_wipe (&loc); - if (xattr) - dict_unref (xattr); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_removexattr, 3.4.0) int -glfs_setxattr (struct glfs *fs, const char *path, const char *name, - const void *value, size_t size, int flags) +pub_glfs_removexattr(struct glfs *fs, const char *path, const char *name) { - return glfs_setxattr_common (fs, path, name, value, size, flags, 1); + return glfs_removexattr_common(fs, path, name, 1); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lremovexattr, 3.4.0) int -glfs_lsetxattr (struct glfs *fs, const char *path, const char *name, - const void *value, size_t size, int flags) +pub_glfs_lremovexattr(struct glfs *fs, const char *path, const char *name) { - return glfs_setxattr_common (fs, path, name, value, size, flags, 0); + return glfs_removexattr_common(fs, path, name, 0); } - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fremovexattr, 3.4.0) int -glfs_fsetxattr (struct glfs_fd *glfd, const char *name, const void *value, - size_t size, int flags) +pub_glfs_fremovexattr(struct glfs_fd *glfd, const char *name) { - int ret = -1; - xlator_t *subvol = NULL; - dict_t *xattr = NULL; - fd_t *fd = NULL; - - __glfs_entry_fd (glfd); + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = syncop_fremovexattr(subvol, fd, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + glfs_subvol_done(glfd->fs, subvol); - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + __GLFS_EXIT_FS; - xattr = dict_for_key_value (name, value, size); - if (!xattr) { - ret = -1; - errno = ENOMEM; - goto out; - } +invalid_fs: + return ret; +} - ret = syncop_fsetxattr (subvol, fd, xattr, flags); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fallocate, 3.5.0) +int +pub_glfs_fallocate(struct glfs_fd *glfd, int keep_size, off_t offset, + size_t len) +{ + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_fallocate(subvol, fd, keep_size, offset, len, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); out: - if (xattr) - dict_unref (xattr); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); - if (fd) - fd_unref (fd); + glfs_subvol_done(glfd->fs, subvol); - glfs_subvol_done (glfd->fs, subvol); + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_discard, 3.5.0) +int +pub_glfs_discard(struct glfs_fd *glfd, off_t offset, size_t len) +{ + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_discard(subvol, fd, offset, len, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); + + glfs_subvol_done(glfd->fs, subvol); + + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_zerofill, 3.5.0) int -glfs_removexattr_common (struct glfs *fs, const char *path, const char *name, - int follow) +pub_glfs_zerofill(struct glfs_fd *glfd, off_t offset, off_t len) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + int ret = -1; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + dict_t *fop_attr = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_zerofill(subvol, fd, offset, len, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + if (fop_attr) + dict_unref(fop_attr); + + glfs_subvol_done(glfd->fs, subvol); - __glfs_entry_fs (fs); + __GLFS_EXIT_FS; - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chdir, 3.4.0) +int +pub_glfs_chdir(struct glfs *fs, const char *path) +{ + int ret = -1; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } retry: - if (follow) - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); - else - ret = glfs_lresolve (fs, subvol, path, &loc, &iatt, reval); + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - if (ret) - goto out; + if (ret) + goto out; - ret = syncop_removexattr (subvol, &loc, name); + if (!IA_ISDIR(iatt.ia_type)) { + ret = -1; + errno = ENOTDIR; + goto out; + } - ESTALE_RETRY (ret, errno, reval, &loc, retry); + glfs_cwd_set(fs, loc.inode); out: - loc_wipe (&loc); + loc_wipe(&loc); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(fs, subvol); - return ret; -} + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchdir, 3.4.0) int -glfs_removexattr (struct glfs *fs, const char *path, const char *name) +pub_glfs_fchdir(struct glfs_fd *glfd) { - return glfs_removexattr_common (fs, path, name, 1); -} + int ret = -1; + inode_t *inode = NULL; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + inode = fd->inode; + + if (!IA_ISDIR(inode->ia_type)) { + ret = -1; + errno = ENOTDIR; + goto out; + } + + glfs_cwd_set(glfd->fs, inode); + ret = 0; +out: + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); + glfs_subvol_done(glfd->fs, subvol); -int -glfs_lremovexattr (struct glfs *fs, const char *path, const char *name) -{ - return glfs_removexattr_common (fs, path, name, 0); + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +static gf_boolean_t warn_realpath = _gf_true; /* log once */ -int -glfs_fremovexattr (struct glfs_fd *glfd, const char *name) +static char * +glfs_realpath_common(struct glfs *fs, const char *path, char *resolved_path, + gf_boolean_t warn_deprecated) { - int ret = -1; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + int ret = -1; + char *retpath = NULL; + char *allocpath = NULL; + xlator_t *subvol = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int reval = 0; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (resolved_path) + retpath = resolved_path; + else if (warn_deprecated) { + retpath = allocpath = malloc(PATH_MAX + 1); + if (warn_realpath) { + warn_realpath = _gf_false; + gf_log(THIS->name, GF_LOG_WARNING, + "this application " + "is compiled against an old version of " + "libgfapi, it should use glfs_free() to " + "release the path returned by " + "glfs_realpath()"); + } + } else { + retpath = allocpath = GLFS_CALLOC(1, PATH_MAX + 1, NULL, + glfs_mt_realpath_t); + } + + if (!retpath) { + ret = -1; + errno = ENOMEM; + goto out; + } + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } +retry: + ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); - __glfs_entry_fd (glfd); + ESTALE_RETRY(ret, errno, reval, &loc, retry); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (ret) + goto out; - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + if (loc.path) { + snprintf(retpath, PATH_MAX + 1, "%s", loc.path); + } - ret = syncop_fremovexattr (subvol, fd, name); out: - if (fd) - fd_unref (fd); + loc_wipe(&loc); + + if (ret == -1) { + if (warn_deprecated && allocpath) + free(allocpath); + else if (allocpath) + GLFS_FREE(allocpath); + retpath = NULL; + } + + glfs_subvol_done(fs, subvol); - glfs_subvol_done (glfd->fs, subvol); + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return retpath; } +GFAPI_SYMVER_PUBLIC(glfs_realpath34, glfs_realpath, 3.4.0) +char * +pub_glfs_realpath34(struct glfs *fs, const char *path, char *resolved_path) +{ + return glfs_realpath_common(fs, path, resolved_path, _gf_true); +} -int -glfs_chdir (struct glfs *fs, const char *path) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_realpath, 3.7.17) +char * +pub_glfs_realpath(struct glfs *fs, const char *path, char *resolved_path) { - int ret = -1; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; + return glfs_realpath_common(fs, path, resolved_path, _gf_false); +} - __glfs_entry_fs (fs); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_getcwd, 3.4.0) +char * +pub_glfs_getcwd(struct glfs *fs, char *buf, size_t n) +{ + int ret = -1; + inode_t *inode = NULL; + char *path = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (!buf || n < 2) { + ret = -1; + errno = EINVAL; + goto out; + } + + inode = glfs_cwd_get(fs); + + if (!inode) { + strncpy(buf, "/", n); + ret = 0; + goto out; + } + + ret = inode_path(inode, 0, &path); + if (n <= ret) { + ret = -1; + errno = ERANGE; + goto out; + } + + strncpy(buf, path, n); + ret = 0; +out: + GF_FREE(path); - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + if (inode) + inode_unref(inode); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + __GLFS_EXIT_FS; - if (ret) - goto out; +invalid_fs: + if (ret < 0) + return NULL; - if (!IA_ISDIR (iatt.ia_type)) { - ret = -1; - errno = ENOTDIR; - goto out; - } + return buf; +} - glfs_cwd_set (fs, loc.inode); +static void +gf_flock_to_flock(struct gf_flock *gf_flock, struct flock *flock) +{ + flock->l_type = gf_flock->l_type; + flock->l_whence = gf_flock->l_whence; + flock->l_start = gf_flock->l_start; + flock->l_len = gf_flock->l_len; + flock->l_pid = gf_flock->l_pid; +} + +static void +gf_flock_from_flock(struct gf_flock *gf_flock, struct flock *flock) +{ + gf_flock->l_type = flock->l_type; + gf_flock->l_whence = flock->l_whence; + gf_flock->l_start = flock->l_start; + gf_flock->l_len = flock->l_len; + gf_flock->l_pid = flock->l_pid; +} + +static int +glfs_lock_common(struct glfs_fd *glfd, int cmd, struct flock *flock, + dict_t *xdata) +{ + int ret = -1; + xlator_t *subvol = NULL; + struct gf_flock gf_flock = { + 0, + }; + struct gf_flock saved_flock = { + 0, + }; + fd_t *fd = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + if (!flock) { + errno = EINVAL; + goto out; + } + + GF_REF_GET(glfd); + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + /* Generate glusterfs flock structure from client flock + * structure to be processed by server */ + gf_flock_from_flock(&gf_flock, flock); + + /* Keep another copy of flock for split/merge of locks + * at client side */ + gf_flock_from_flock(&saved_flock, flock); + + if (glfd->lk_owner.len != 0) { + ret = syncopctx_setfslkowner(&glfd->lk_owner); + + if (ret) + goto out; + } + + ret = get_fop_attr_thrd_key(&xdata); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_lk(subvol, fd, cmd, &gf_flock, xdata, NULL); + DECODE_SYNCOP_ERR(ret); + + /* Convert back from gf_flock to flock as expected by application */ + gf_flock_to_flock(&gf_flock, flock); + + if (ret == 0 && (cmd == F_SETLK || cmd == F_SETLKW)) { + ret = fd_lk_insert_and_merge(fd, cmd, &saved_flock); + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, 0, + API_MSG_LOCK_INSERT_MERGE_FAILED, "gfid=%s", + uuid_utoa(fd->inode->gfid), NULL); + ret = 0; + } + } out: - loc_wipe (&loc); + if (fd) + fd_unref(fd); + if (glfd) + GF_REF_PUT(glfd); - glfs_subvol_done (fs, subvol); + glfs_subvol_done(glfd->fs, subvol); - return ret; + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_file_lock, 4.0.0) +int +pub_glfs_file_lock(struct glfs_fd *glfd, int cmd, struct flock *flock, + glfs_lock_mode_t lk_mode) +{ + int ret = -1; + dict_t *xdata_in = NULL; + + if (lk_mode == GLFS_LK_MANDATORY) { + /* Create a new dictionary */ + xdata_in = dict_new(); + if (xdata_in == NULL) { + ret = -1; + errno = ENOMEM; + goto out; + } + + /* Set GF_LK_MANDATORY internally within dictionary to map + * GLFS_LK_MANDATORY */ + ret = dict_set_uint32(xdata_in, GF_LOCK_MODE, GF_LK_MANDATORY); + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, 0, + API_MSG_SETTING_LOCK_TYPE_FAILED, NULL); + ret = -1; + errno = ENOMEM; + goto out; + } + } + + ret = glfs_lock_common(glfd, cmd, flock, xdata_in); +out: + if (xdata_in) + dict_unref(xdata_in); + + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_posix_lock, 3.4.0) +int +pub_glfs_posix_lock(struct glfs_fd *glfd, int cmd, struct flock *flock) +{ + return glfs_lock_common(glfd, cmd, flock, NULL); +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fd_set_lkowner, 3.10.7) int -glfs_fchdir (struct glfs_fd *glfd) +pub_glfs_fd_set_lkowner(struct glfs_fd *glfd, void *data, int len) { - int ret = -1; - inode_t *inode = NULL; - xlator_t *subvol = NULL; - fd_t *fd = NULL; + int ret = -1; - __glfs_entry_fd (glfd); + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + if (!GF_REF_GET(glfd)) { + goto invalid_fs; + } - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + GF_VALIDATE_OR_GOTO(THIS->name, data, out); - inode = fd->inode; + if ((len <= 0) || (len > GFAPI_MAX_LOCK_OWNER_LEN)) { + errno = EINVAL; + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, + "lk_owner len=%d", len, NULL); + goto out; + } - if (!IA_ISDIR (inode->ia_type)) { - ret = -1; - errno = ENOTDIR; - goto out; - } + glfd->lk_owner.len = len; - glfs_cwd_set (glfd->fs, inode); - ret = 0; + memcpy(glfd->lk_owner.data, data, len); + + ret = 0; out: - if (fd) - fd_unref (fd); + if (glfd) + GF_REF_PUT(glfd); - glfs_subvol_done (glfd->fs, subvol); + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_dup, 3.4.0) +struct glfs_fd * +pub_glfs_dup(struct glfs_fd *glfd) +{ + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct glfs_fd *dupfd = NULL; + struct glfs *fs = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + fs = glfd->fs; + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(fs, subvol, glfd); + if (!fd) { + errno = EBADFD; + goto out; + } + + dupfd = glfs_fd_new(fs); + if (!dupfd) { + errno = ENOMEM; + goto out; + } + + dupfd->fd = fd_ref(fd); + dupfd->state = glfd->state; +out: + if (fd) + fd_unref(fd); + if (dupfd) + glfs_fd_bind(dupfd); + if (glfd) + GF_REF_PUT(glfd); -char * -glfs_realpath (struct glfs *fs, const char *path, char *resolved_path) -{ - int ret = -1; - char *retpath = NULL; - char *allocpath = NULL; - xlator_t *subvol = NULL; - loc_t loc = {0, }; - struct iatt iatt = {0, }; - int reval = 0; - - __glfs_entry_fs (fs); - - if (resolved_path) - retpath = resolved_path; - else - retpath = allocpath = malloc (PATH_MAX + 1); - - if (!retpath) { - ret = -1; - errno = ENOMEM; - goto out; - } - - subvol = glfs_active_subvol (fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } -retry: - ret = glfs_resolve (fs, subvol, path, &loc, &iatt, reval); + glfs_subvol_done(fs, subvol); - ESTALE_RETRY (ret, errno, reval, &loc, retry); + __GLFS_EXIT_FS; - if (ret) - goto out; +invalid_fs: + return dupfd; +} - if (loc.path) { - strncpy (retpath, loc.path, PATH_MAX); - retpath[PATH_MAX] = 0; - } +static void +glfs_enqueue_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) +{ + int ret = -1; + upcall_entry *u_list = NULL; + + if (!fs || !upcall_data) + goto out; + + u_list = GF_CALLOC(1, sizeof(*u_list), glfs_mt_upcall_entry_t); + + if (!u_list) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } + + INIT_LIST_HEAD(&u_list->upcall_list); + + gf_uuid_copy(u_list->upcall_data.gfid, upcall_data->gfid); + u_list->upcall_data.event_type = upcall_data->event_type; + + switch (upcall_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + ret = glfs_get_upcall_cache_invalidation(&u_list->upcall_data, + upcall_data); + break; + case GF_UPCALL_RECALL_LEASE: + ret = glfs_get_upcall_lease(&u_list->upcall_data, upcall_data); + break; + default: + break; + } + + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ENTRY, NULL); + goto out; + } + + pthread_mutex_lock(&fs->upcall_list_mutex); + { + list_add_tail(&u_list->upcall_list, &fs->upcall_list); + } + pthread_mutex_unlock(&fs->upcall_list_mutex); + + ret = 0; out: - loc_wipe (&loc); + if (ret && u_list) { + GF_FREE(u_list->upcall_data.data); + GF_FREE(u_list); + } +} - if (ret == -1) { - if (allocpath) - free (allocpath); - retpath = NULL; - } +static void +glfs_free_upcall_lease(void *to_free) +{ + struct glfs_upcall_lease *arg = to_free; + + if (!arg) + return; - glfs_subvol_done (fs, subvol); + if (arg->object) + glfs_h_close(arg->object); - return retpath; + GF_FREE(arg); } +int +glfs_recall_lease_fd(struct glfs *fs, struct gf_upcall *up_data) +{ + struct gf_upcall_recall_lease *recall_lease = NULL; + xlator_t *subvol = NULL; + int ret = 0; + inode_t *inode = NULL; + struct glfs_fd *glfd = NULL; + struct glfs_fd *tmp = NULL; + struct list_head glfd_list; + fd_t *fd = NULL; + uint64_t value = 0; + struct glfs_lease lease = { + 0, + }; + + GF_VALIDATE_OR_GOTO("gfapi", up_data, out); + GF_VALIDATE_OR_GOTO("gfapi", fs, out); + + recall_lease = up_data->data; + GF_VALIDATE_OR_GOTO("gfapi", recall_lease, out); + + INIT_LIST_HEAD(&glfd_list); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + gf_msg_debug(THIS->name, 0, "Recall lease received for gfid:%s", + uuid_utoa(up_data->gfid)); + + inode = inode_find(subvol->itable, up_data->gfid); + if (!inode) { + ret = -1; + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INODE_FIND_FAILED, + "gfid=%s", uuid_utoa(up_data->gfid), "graph_id=%d", + subvol->graph->id, NULL); + goto out; + } + + LOCK(&inode->lock); + { + list_for_each_entry(fd, &inode->fd_list, inode_list) + { + ret = fd_ctx_get(fd, subvol, &value); + glfd = (struct glfs_fd *)(uintptr_t)value; + if (glfd) { + gf_msg_trace(THIS->name, 0, "glfd (%p) has held lease", glfd); + GF_REF_GET(glfd); + list_add_tail(&glfd->list, &glfd_list); + } + } + } + UNLOCK(&inode->lock); + + if (!list_empty(&glfd_list)) { + list_for_each_entry_safe(glfd, tmp, &glfd_list, list) + { + LOCK(&glfd->lock); + { + if (glfd->state != GLFD_CLOSE) { + gf_msg_trace(THIS->name, 0, + "glfd (%p) has held lease, " + "calling recall cbk", + glfd); + glfd->cbk(lease, glfd->cookie); + } + } + UNLOCK(&glfd->lock); + + list_del_init(&glfd->list); + GF_REF_PUT(glfd); + } + } -char * -glfs_getcwd (struct glfs *fs, char *buf, size_t n) +out: + return ret; +} + +static int +glfs_recall_lease_upcall(struct glfs *fs, struct glfs_upcall *up_arg, + struct gf_upcall *up_data) { - int ret = -1; - inode_t *inode = NULL; - char *path = NULL; + struct gf_upcall_recall_lease *recall_lease = NULL; + struct glfs_object *object = NULL; + xlator_t *subvol = NULL; + int ret = -1; + struct glfs_upcall_lease *up_lease_arg = NULL; + + GF_VALIDATE_OR_GOTO("gfapi", up_data, out); + GF_VALIDATE_OR_GOTO("gfapi", fs, out); + + recall_lease = up_data->data; + GF_VALIDATE_OR_GOTO("gfapi", recall_lease, out); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + gf_msg_debug(THIS->name, 0, "Recall lease received for gfid:%s", + uuid_utoa(up_data->gfid)); + + object = glfs_h_find_handle(fs, up_data->gfid, GFAPI_HANDLE_LENGTH); + if (!object) { + /* The reason handle creation will fail is because we + * couldn't find the inode in the gfapi inode table. + * + * But since application would have taken inode_ref, the + * only case when this can happen is when it has closed + * the handle and hence will no more be interested in + * the upcall for this particular gfid. + */ + gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, + "gfid=%s", uuid_utoa(up_data->gfid), NULL); + errno = ESTALE; + goto out; + } + + up_lease_arg = GF_CALLOC(1, sizeof(struct glfs_upcall_lease), + glfs_mt_upcall_inode_t); + up_lease_arg->object = object; + + GF_VALIDATE_OR_GOTO("glfs_recall_lease", up_lease_arg, out); + + up_lease_arg->lease_type = recall_lease->lease_type; + + up_arg->reason = GLFS_UPCALL_RECALL_LEASE; + up_arg->event = up_lease_arg; + up_arg->free_event = glfs_free_upcall_lease; + + ret = 0; - __glfs_entry_fs (fs); +out: + if (ret) { + /* Close p_object and oldp_object as well if being referenced.*/ + if (object) + glfs_h_close(object); + + /* Set reason to prevent applications from using ->event */ + up_arg->reason = GF_UPCALL_EVENT_NULL; + } + return ret; +} - if (!buf || n < 2) { - ret = -1; - errno = EINVAL; - goto out; - } +static int +upcall_syncop_args_free(struct upcall_syncop_args *args) +{ + dict_t *dict = NULL; + struct gf_upcall *upcall_data = NULL; + + if (args) { + upcall_data = &args->upcall_data; + switch (upcall_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + dict = ((struct gf_upcall_cache_invalidation *)(upcall_data + ->data)) + ->dict; + break; + case GF_UPCALL_RECALL_LEASE: + dict = ((struct gf_upcall_recall_lease *)(upcall_data->data)) + ->dict; + break; + } + if (dict) + dict_unref(dict); + + GF_FREE(upcall_data->client_uid); + GF_FREE(upcall_data->data); + } + GF_FREE(args); + return 0; +} - inode = glfs_cwd_get (fs); +static int +glfs_upcall_syncop_cbk(int ret, call_frame_t *frame, void *opaque) +{ + struct upcall_syncop_args *args = opaque; - if (!inode) { - strncpy (buf, "/", n); - ret = 0; - goto out; - } + (void)upcall_syncop_args_free(args); - ret = inode_path (inode, 0, &path); - if (n <= ret) { - ret = -1; - errno = ERANGE; - goto out; - } + return 0; +} + +static int +glfs_cbk_upcall_syncop(void *opaque) +{ + struct upcall_syncop_args *args = opaque; + struct gf_upcall *upcall_data = NULL; + struct glfs_upcall *up_arg = NULL; + struct glfs *fs; + int ret = -1; + + fs = args->fs; + upcall_data = &args->upcall_data; + + if (!upcall_data) { + goto out; + } + + up_arg = GLFS_CALLOC(1, sizeof(struct gf_upcall), glfs_release_upcall, + glfs_mt_upcall_entry_t); + if (!up_arg) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } + + switch (upcall_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + ret = glfs_h_poll_cache_invalidation(fs, up_arg, upcall_data); + break; + case GF_UPCALL_RECALL_LEASE: + ret = glfs_recall_lease_upcall(fs, up_arg, upcall_data); + break; + default: + errno = EINVAL; + } + + /* It could so happen that the file which got + * upcall notification may have got deleted by + * the same client. In such cases up_arg->reason + * is set to GLFS_UPCALL_EVENT_NULL. No need to + * send upcall then + */ + if (up_arg->reason == GLFS_UPCALL_EVENT_NULL) { + gf_smsg(THIS->name, GF_LOG_DEBUG, errno, + API_MSG_UPCALL_EVENT_NULL_RECEIVED, NULL); + ret = 0; + GLFS_FREE(up_arg); + goto out; + } else if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ENTRY, NULL); + GLFS_FREE(up_arg); + goto out; + } + + if (fs->up_cbk && up_arg) + (fs->up_cbk)(up_arg, fs->up_data); + + /* application takes care of calling glfs_free on up_arg post + * their processing */ - strncpy (buf, path, n); - ret = 0; out: - GF_FREE (path); + return ret; +} - if (inode) - inode_unref (inode); +static struct gf_upcall_cache_invalidation * +gf_copy_cache_invalidation(struct gf_upcall_cache_invalidation *src) +{ + struct gf_upcall_cache_invalidation *dst = NULL; - if (ret < 0) - return NULL; + if (!src) + goto out; - return buf; -} + dst = GF_CALLOC(1, sizeof(struct gf_upcall_cache_invalidation), + glfs_mt_upcall_entry_t); + if (!dst) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } -static void -gf_flock_to_flock (struct gf_flock *gf_flock, struct flock *flock) + dst->flags = src->flags; + dst->expire_time_attr = src->expire_time_attr; + dst->stat = src->stat; + dst->p_stat = src->p_stat; + dst->oldp_stat = src->oldp_stat; + + if (src->dict) + dst->dict = dict_copy_with_ref(src->dict, NULL); + + return dst; +out: + return NULL; +} + +static struct gf_upcall_recall_lease * +gf_copy_recall_lease(struct gf_upcall_recall_lease *src) { - flock->l_type = gf_flock->l_type; - flock->l_whence = gf_flock->l_whence; - flock->l_start = gf_flock->l_start; - flock->l_len = gf_flock->l_len; - flock->l_pid = gf_flock->l_pid; + struct gf_upcall_recall_lease *dst = NULL; + + if (!src) + goto out; + + dst = GF_CALLOC(1, sizeof(struct gf_upcall_recall_lease), + glfs_mt_upcall_entry_t); + + if (!dst) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", + NULL); + goto out; + } + + dst->lease_type = src->lease_type; + memcpy(dst->tid, src->tid, 16); + + if (src->dict) + dst->dict = dict_copy_with_ref(src->dict, NULL); + + return dst; +out: + return NULL; } +static struct upcall_syncop_args * +upcall_syncop_args_init(struct glfs *fs, struct gf_upcall *upcall_data) +{ + struct upcall_syncop_args *args = NULL; + int ret = -1; + struct gf_upcall *t_data = NULL; + + if (!fs || !upcall_data) + goto out; + + args = GF_CALLOC(1, sizeof(struct upcall_syncop_args), + glfs_mt_upcall_entry_t); + if (!args) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, + "syncop args", NULL); + goto out; + } + + /* Note: we are not taking any ref on fs here. + * Ideally applications have to unregister for upcall events + * or stop polling for upcall events before performing + * glfs_fini. And as for outstanding synctasks created, we wait + * for all syncenv threads to finish tasks before cleaning up the + * fs->ctx. Hence it seems safe to process these callback + * notification without taking any lock/ref. + */ + args->fs = fs; + t_data = &(args->upcall_data); + t_data->client_uid = gf_strdup(upcall_data->client_uid); + + gf_uuid_copy(t_data->gfid, upcall_data->gfid); + t_data->event_type = upcall_data->event_type; + + switch (t_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + t_data->data = gf_copy_cache_invalidation( + (struct gf_upcall_cache_invalidation *)upcall_data->data); + break; + case GF_UPCALL_RECALL_LEASE: + t_data->data = gf_copy_recall_lease( + (struct gf_upcall_recall_lease *)upcall_data->data); + break; + } + + if (!t_data->data) + goto out; + + return args; +out: + if (ret) { + if (args) { + GF_FREE(args->upcall_data.client_uid); + GF_FREE(args); + } + } + + return NULL; +} static void -gf_flock_from_flock (struct gf_flock *gf_flock, struct flock *flock) +glfs_cbk_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) { - gf_flock->l_type = flock->l_type; - gf_flock->l_whence = flock->l_whence; - gf_flock->l_start = flock->l_start; - gf_flock->l_len = flock->l_len; - gf_flock->l_pid = flock->l_pid; + struct upcall_syncop_args *args = NULL; + int ret = -1; + + if (!fs || !upcall_data) + goto out; + + if (!(fs->upcall_events & upcall_data->event_type)) { + /* ignore events which application hasn't registered*/ + goto out; + } + + args = upcall_syncop_args_init(fs, upcall_data); + + if (!args) + goto out; + + ret = synctask_new(THIS->ctx->env, glfs_cbk_upcall_syncop, + glfs_upcall_syncop_cbk, NULL, args); + /* should we retry incase of failure? */ + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_UPCALL_SYNCOP_FAILED, + "event_type=%d", upcall_data->event_type, "gfid=%s", + (char *)(upcall_data->gfid), NULL); + upcall_syncop_args_free(args); + } + +out: + return; } +/* + * This routine is called in case of any notification received + * from the server. All the upcall events are queued up in a list + * to be read by the applications. + * + * In case if the application registers a cbk function, that shall + * be called by this routine in case of any event received. + * The cbk fn is responsible for notifying the + * applications the way it desires for each event queued (for eg., + * can raise a signal or broadcast a cond variable etc.) + * + * Otherwise all the upcall events are queued up in a list + * to be read/polled by the applications. + */ +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_process_upcall_event, 3.7.0) +void +priv_glfs_process_upcall_event(struct glfs *fs, void *data) +{ + glusterfs_ctx_t *ctx = NULL; + struct gf_upcall *upcall_data = NULL; + + DECLARE_OLD_THIS; + + gf_msg_debug(THIS->name, 0, "Upcall gfapi callback is called"); + + __GLFS_ENTRY_VALIDATE_FS(fs, err); + + if (!data) + goto out; + + /* Unlike in I/O path, "glfs_fini" would not have freed + * 'fs' by the time we take lock as it waits for all epoll + * threads to exit including this + */ + pthread_mutex_lock(&fs->mutex); + { + ctx = fs->ctx; + + /* if we're not interested in upcalls (anymore), skip them */ + if (ctx->cleanup_started || !fs->cache_upcalls) { + pthread_mutex_unlock(&fs->mutex); + goto out; + } + + fs->pin_refcnt++; + } + pthread_mutex_unlock(&fs->mutex); + + upcall_data = (struct gf_upcall *)data; + + gf_msg_trace(THIS->name, 0, "Upcall gfapi gfid = %s", + (char *)(upcall_data->gfid)); + + /* * + * TODO: RECALL LEASE for each glfd + * + * In case of RECALL_LEASE, we could associate separate + * cbk function for each glfd either by + * - extending pub_glfs_lease to accept new args (recall_cbk_fn, cookie) + * - or by defining new API "glfs_register_recall_cbk_fn (glfd, + * recall_cbk_fn, cookie) . In such cases, flag it and instead of calling + * below upcall functions, define a new one to go through the glfd list and + * invoke each of theirs recall_cbk_fn. + * */ + + if (fs->up_cbk) { /* upcall cbk registered */ + (void)glfs_cbk_upcall_data(fs, upcall_data); + } else { + (void)glfs_enqueue_upcall_data(fs, upcall_data); + } + + pthread_mutex_lock(&fs->mutex); + { + fs->pin_refcnt--; + } + pthread_mutex_unlock(&fs->mutex); -int -glfs_posix_lock (struct glfs_fd *glfd, int cmd, struct flock *flock) +out: + __GLFS_EXIT_FS; +err: + return; +} + +ssize_t +glfs_anonymous_pwritev(struct glfs *fs, struct glfs_object *object, + const struct iovec *iovec, int iovcnt, off_t offset, + int flags) { - int ret = -1; - xlator_t *subvol = NULL; - struct gf_flock gf_flock = {0, }; - struct gf_flock saved_flock = {0, }; - fd_t *fd = NULL; + xlator_t *subvol = NULL; + struct iobref *iobref = NULL; + struct iobuf *iobuf = NULL; + struct iovec iov = { + 0, + }; + inode_t *inode = NULL; + fd_t *fd = NULL; + int ret = -1; + size_t size = -1; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + ret = -1; + errno = ESTALE; + goto out; + } + + fd = fd_anonymous(inode); + if (!fd) { + ret = -1; + gf_smsg("gfapi", GF_LOG_ERROR, ENOMEM, API_MSG_FDCREATE_FAILED, NULL); + errno = ENOMEM; + goto out; + } + + size = iov_length(iovec, iovcnt); + + iobuf = iobuf_get2(subvol->ctx->iobuf_pool, size); + if (!iobuf) { + ret = -1; + errno = ENOMEM; + goto out; + } + + iobref = iobref_new(); + if (!iobref) { + iobuf_unref(iobuf); + errno = ENOMEM; + ret = -1; + goto out; + } + + ret = iobref_add(iobref, iobuf); + if (ret) { + iobuf_unref(iobuf); + iobref_unref(iobref); + errno = ENOMEM; + ret = -1; + goto out; + } + + iov_unload(iobuf_ptr(iobuf), iovec, iovcnt); + + iov.iov_base = iobuf_ptr(iobuf); + iov.iov_len = size; + + /* TODO : set leaseid */ + ret = syncop_writev(subvol, fd, &iov, 1, offset, iobref, flags, NULL, NULL, + NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + iobuf_unref(iobuf); + iobref_unref(iobref); + + if (ret <= 0) + goto out; + +out: + + if (fd) + fd_unref(fd); - __glfs_entry_fd (glfd); + if (inode) + inode_unref(inode); - subvol = glfs_active_subvol (glfd->fs); - if (!subvol) { - ret = -1; - errno = EIO; - goto out; - } + glfs_subvol_done(fs, subvol); - fd = glfs_resolve_fd (glfd->fs, subvol, glfd); - if (!fd) { - ret = -1; - errno = EBADFD; - goto out; - } + __GLFS_EXIT_FS; - gf_flock_from_flock (&gf_flock, flock); - gf_flock_from_flock (&saved_flock, flock); - ret = syncop_lk (subvol, fd, cmd, &gf_flock); - gf_flock_to_flock (&gf_flock, flock); +invalid_fs: + return ret; +} - if (ret == 0 && (cmd == F_SETLK || cmd == F_SETLKW)) - fd_lk_insert_and_merge (fd, cmd, &saved_flock); +ssize_t +glfs_anonymous_preadv(struct glfs *fs, struct glfs_object *object, + const struct iovec *iovec, int iovcnt, off_t offset, + int flags) +{ + xlator_t *subvol = NULL; + struct iovec *iov = NULL; + struct iobref *iobref = NULL; + inode_t *inode = NULL; + fd_t *fd = NULL; + int cnt = 0; + ssize_t ret = -1; + ssize_t size = -1; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + ret = -1; + errno = ESTALE; + goto out; + } + + fd = fd_anonymous(inode); + if (!fd) { + ret = -1; + gf_smsg("gfapi", GF_LOG_ERROR, ENOMEM, API_MSG_FDCREATE_FAILED, NULL); + errno = ENOMEM; + goto out; + } + + size = iov_length(iovec, iovcnt); + + /* TODO : set leaseid */ + ret = syncop_readv(subvol, fd, size, offset, flags, &iov, &cnt, &iobref, + NULL, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret <= 0) + goto out; + + size = iov_copy(iovec, iovcnt, iov, cnt); + + ret = size; out: - if (fd) - fd_unref (fd); + if (iov) + GF_FREE(iov); + if (iobref) + iobref_unref(iobref); + if (fd) + fd_unref(fd); - glfs_subvol_done (glfd->fs, subvol); + if (inode) + inode_unref(inode); - return ret; + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +static void +glfs_release_xreaddirp_stat(void *ptr) +{ + struct glfs_xreaddirp_stat *to_free = ptr; -struct glfs_fd * -glfs_dup (struct glfs_fd *glfd) + if (to_free->object) + glfs_h_close(to_free->object); +} + +/* + * Given glfd of a directory, this function does readdirp and returns + * xstat along with dirents. + */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_r, 3.11.0) +int +pub_glfs_xreaddirplus_r(struct glfs_fd *glfd, uint32_t flags, + struct glfs_xreaddirp_stat **xstat_p, + struct dirent *ext, struct dirent **res) { - xlator_t *subvol = NULL; - fd_t *fd = NULL; - glfs_fd_t *dupfd = NULL; - struct glfs *fs = NULL; + int ret = -1; + gf_dirent_t *entry = NULL; + struct dirent *buf = NULL; + struct glfs_xreaddirp_stat *xstat = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + GF_VALIDATE_OR_GOTO(THIS->name, xstat_p, out); + GF_VALIDATE_OR_GOTO(THIS->name, res, out); + + errno = 0; + + if (ext) + buf = ext; + else + buf = glfs_readdirbuf_get(glfd); + + if (!buf) + goto out; + + xstat = GLFS_CALLOC(1, sizeof(struct glfs_xreaddirp_stat), + glfs_release_xreaddirp_stat, glfs_mt_xreaddirp_stat_t); + + if (!xstat) + goto out; + + /* this is readdirplus operation */ + entry = glfd_entry_next(glfd, 1); + + /* XXX: Ideally when we reach EOD, errno should have been + * set to ENOENT. But that doesn't seem to be the case. + * + * The only way to confirm if its EOD at this point is that + * errno == 0 and entry == NULL + */ + if (errno) + goto out; + + if (!entry) { + /* reached EOD, ret = 0 */ + ret = 0; + *res = NULL; + *xstat_p = NULL; + + /* free xstat as applications shall not be using it */ + GLFS_FREE(xstat); + + goto out; + } + + *res = buf; + gf_dirent_to_dirent(entry, buf); + + if (flags & GFAPI_XREADDIRP_STAT) { + glfs_iatt_to_stat(glfd->fs, &entry->d_stat, &xstat->st); + xstat->flags_handled |= GFAPI_XREADDIRP_STAT; + } + + if ((flags & GFAPI_XREADDIRP_HANDLE) && + /* skip . and .. */ + strcmp(buf->d_name, ".") && strcmp(buf->d_name, "..")) { + /* Now create object. + * We can use "glfs_h_find_handle" as well as inodes would have + * already got linked as part of 'gf_link_inodes_from_dirent' */ + xstat->object = glfs_h_create_from_handle( + glfd->fs, entry->d_stat.ia_gfid, GFAPI_HANDLE_LENGTH, NULL); + + if (xstat->object) { /* success */ + /* note: xstat->object->inode->ref is taken + * This shall be unref'ed when application does + * glfs_free(xstat) */ + xstat->flags_handled |= GFAPI_XREADDIRP_HANDLE; + } + } + + ret = xstat->flags_handled; + *xstat_p = xstat; + + gf_msg_debug(THIS->name, 0, + "xreaddirp- requested_flags (%x) , processed_flags (%x)", + flags, xstat->flags_handled); - __glfs_entry_fd (glfd); +out: + GF_REF_PUT(glfd); - fs = glfd->fs; - subvol = glfs_active_subvol (fs); - if (!subvol) { - errno = EIO; - goto out; - } + if (ret < 0) { + gf_smsg(THIS->name, GF_LOG_WARNING, errno, API_MSG_XREADDIRP_R_FAILED, + "reason=%s", strerror(errno), NULL); - fd = glfs_resolve_fd (fs, subvol, glfd); - if (!fd) { - errno = EBADFD; - goto out; - } + if (xstat) + GLFS_FREE(xstat); + } - dupfd = glfs_fd_new (fs); - if (!dupfd) { - errno = ENOMEM; - goto out; - } + __GLFS_EXIT_FS; + + return ret; + +invalid_fs: + return -1; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_get_stat, 3.11.0) +struct stat * +pub_glfs_xreaddirplus_get_stat(struct glfs_xreaddirp_stat *xstat) +{ + GF_VALIDATE_OR_GOTO("glfs_xreaddirplus_get_stat", xstat, out); + + if (!xstat->flags_handled & GFAPI_XREADDIRP_STAT) + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_FLAGS_HANDLE, + "GFAPI_XREADDIRP_STAT" + "xstat=%p", + xstat, "handles=%x", xstat->flags_handled, NULL); + return &xstat->st; + +out: + return NULL; +} + +void +gf_lease_to_glfs_lease(struct gf_lease *gf_lease, struct glfs_lease *lease) +{ + u_int lease_type = gf_lease->lease_type; + lease->cmd = gf_lease->cmd; + lease->lease_type = lease_type; + memcpy(lease->lease_id, gf_lease->lease_id, LEASE_ID_SIZE); +} + +void +glfs_lease_to_gf_lease(struct glfs_lease *lease, struct gf_lease *gf_lease) +{ + u_int lease_type = lease->lease_type; + gf_lease->cmd = lease->cmd; + gf_lease->lease_type = lease_type; + memcpy(gf_lease->lease_id, lease->lease_id, LEASE_ID_SIZE); +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lease, 4.0.0) +int +pub_glfs_lease(struct glfs_fd *glfd, struct glfs_lease *lease, + glfs_recall_cbk fn, void *data) +{ + int ret = -1; + loc_t loc = { + 0, + }; + xlator_t *subvol = NULL; + fd_t *fd = NULL; + struct gf_lease gf_lease = { + 0, + }; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); + + GF_REF_GET(glfd); + + if (!is_valid_lease_id(lease->lease_id)) { + ret = -1; + errno = EINVAL; + goto out; + } + + subvol = glfs_active_subvol(glfd->fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + fd = glfs_resolve_fd(glfd->fs, subvol, glfd); + if (!fd) { + ret = -1; + errno = EBADFD; + goto out; + } + + switch (lease->lease_type) { + case GLFS_RD_LEASE: + if ((fd->flags != O_RDONLY) && !(fd->flags & O_RDWR)) { + ret = -1; + errno = EINVAL; + goto out; + } + break; + case GLFS_RW_LEASE: + if (!((fd->flags & O_WRONLY) || (fd->flags & O_RDWR))) { + ret = -1; + errno = EINVAL; + goto out; + } + break; + default: + if (lease->cmd != GLFS_GET_LEASE) { + ret = -1; + errno = EINVAL; + goto out; + } + break; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(fd->inode, loc, out); + + glfs_lease_to_gf_lease(lease, &gf_lease); + + ret = syncop_lease(subvol, &loc, &gf_lease, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + gf_lease_to_glfs_lease(&gf_lease, lease); + + /* TODO: Add leases for client replay + if (ret == 0 && (cmd == F_SETLK || cmd == F_SETLKW)) + fd_lk_insert_and_merge (fd, cmd, &saved_flock); + */ + if (ret == 0) { + ret = fd_ctx_set(glfd->fd, subvol, (uint64_t)(long)glfd); + if (ret) { + gf_smsg(subvol->name, GF_LOG_ERROR, ENOMEM, + API_MSG_FDCTX_SET_FAILED, "fd=%p", glfd->fd, NULL); + goto out; + } + glfd->cbk = fn; + glfd->cookie = data; + } - dupfd->fd = fd_ref (fd); out: - if (fd) - fd_unref (fd); - if (dupfd) - glfs_fd_bind (dupfd); - glfs_subvol_done (fs, subvol); + if (glfd) + GF_REF_PUT(glfd); + + if (subvol) + glfs_subvol_done(glfd->fs, subvol); + + __GLFS_EXIT_FS; - return dupfd; +invalid_fs: + return ret; } diff --git a/api/src/glfs-handleops.c b/api/src/glfs-handleops.c new file mode 100644 index 00000000000..53c2ee896f9 --- /dev/null +++ b/api/src/glfs-handleops.c @@ -0,0 +1,2655 @@ +/* + * Copyright (c) 2013-2018 Red Hat, Inc. <http://www.redhat.com> + * This file is part of GlusterFS. + * + * This file is licensed to you under your choice of the GNU Lesser + * General Public License, version 3 or any later version (LGPLv3 or + * later), or the GNU General Public License, version 2 (GPLv2), in all + * cases as published by the Free Software Foundation. + */ + +#include "glfs-internal.h" +#include "glfs-mem-types.h" +#include <glusterfs/syncop.h> +#include "glfs.h" +#include "glfs-handles.h" +#include "gfapi-messages.h" + +int +glfs_listxattr_process(void *value, size_t size, dict_t *xattr); + +void +glfs_iatt_from_stat(struct stat *stat, int valid, struct iatt *iatt, + int *glvalid) +{ + /* validate in args */ + if ((stat == NULL) || (iatt == NULL) || (glvalid == NULL)) { + errno = EINVAL; + return; + } + + *glvalid = 0; + + if (valid & GFAPI_SET_ATTR_MODE) { + iatt->ia_prot = ia_prot_from_st_mode(stat->st_mode); + *glvalid |= GF_SET_ATTR_MODE; + } + + if (valid & GFAPI_SET_ATTR_UID) { + iatt->ia_uid = stat->st_uid; + *glvalid |= GF_SET_ATTR_UID; + } + + if (valid & GFAPI_SET_ATTR_GID) { + iatt->ia_gid = stat->st_gid; + *glvalid |= GF_SET_ATTR_GID; + } + + if (valid & GFAPI_SET_ATTR_ATIME) { + iatt->ia_atime = stat->st_atime; + iatt->ia_atime_nsec = ST_ATIM_NSEC(stat); + *glvalid |= GF_SET_ATTR_ATIME; + } + + if (valid & GFAPI_SET_ATTR_MTIME) { + iatt->ia_mtime = stat->st_mtime; + iatt->ia_mtime_nsec = ST_MTIM_NSEC(stat); + *glvalid |= GF_SET_ATTR_MTIME; + } + + return; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_lookupat, 3.7.4) +struct glfs_object * +pub_glfs_h_lookupat(struct glfs *fs, struct glfs_object *parent, + const char *path, struct stat *stat, int follow) +{ + int ret = 0; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + struct iatt iatt = { + 0, + }; + struct glfs_object *object = NULL; + loc_t loc = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if (path == NULL) { + errno = EINVAL; + return NULL; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + if (parent) { + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + errno = ESTALE; + goto out; + } + } + + /* fop/op */ + ret = glfs_resolve_at(fs, subvol, inode, path, &loc, &iatt, follow, 0); + + /* populate out args */ + if (!ret) { + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC(glfs_h_lookupat34, glfs_h_lookupat, 3.4.2) +struct glfs_object * +pub_glfs_h_lookupat34(struct glfs *fs, struct glfs_object *parent, + const char *path, struct stat *stat) +{ + return pub_glfs_h_lookupat(fs, parent, path, stat, 0); +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_statfs, 3.7.0) +int +pub_glfs_h_statfs(struct glfs *fs, struct glfs_object *object, + struct statvfs *statvfs) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((fs == NULL) || (object == NULL || statvfs == NULL)) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_statfs(subvol, &loc, statvfs, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + loc_wipe(&loc); + +out: + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_stat, 3.4.2) +int +pub_glfs_h_stat(struct glfs *fs, struct glfs_object *object, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_stat(subvol, &loc, &iatt, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (!ret && stat) { + glfs_iatt_to_stat(fs, &iatt, stat); + } +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_getattrs, 3.4.2) +int +pub_glfs_h_getattrs(struct glfs *fs, struct glfs_object *object, + struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + struct iatt iatt = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + ret = 0; + errno = ESTALE; + goto out; + } + + /* fop/op */ + ret = glfs_resolve_base(fs, subvol, inode, &iatt); + + /* populate out args */ + if (!ret && stat) { + glfs_iatt_to_stat(fs, &iatt, stat); + } + +out: + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +int +glfs_h_getxattrs_common(struct glfs *fs, struct glfs_object *object, + dict_t **xattr, const char *name, + gf_boolean_t is_listxattr) +{ + int ret = 0; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + if (!is_listxattr) { + if (!name || *name == '\0') { + errno = EINVAL; + return -1; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + errno = ENAMETOOLONG; + return -1; + } + } + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + ret = syncop_getxattr(subvol, &loc, xattr, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_getxattrs, 3.5.1) +int +pub_glfs_h_getxattrs(struct glfs *fs, struct glfs_object *object, + const char *name, void *value, size_t size) +{ + int ret = -1; + dict_t *xattr = NULL; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + ret = glfs_h_getxattrs_common(fs, object, &xattr, name, (name == NULL)); + if (ret) + goto out; + + /* If @name is NULL, means get all the xattrs (i.e listxattr). */ + if (name) + ret = glfs_getxattr_process(value, size, xattr, name); + else + ret = glfs_listxattr_process(value, size, xattr); + +out: + if (xattr) + dict_unref(xattr); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_setattrs, 3.4.2) +int +pub_glfs_h_setattrs(struct glfs *fs, struct glfs_object *object, + struct stat *stat, int valid) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + int glvalid = 0; + + /* validate in args */ + if ((fs == NULL) || (object == NULL) || (stat == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* map valid masks from in args */ + glfs_iatt_from_stat(stat, valid, &iatt, &glvalid); + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_setxattrs, 3.5.0) +int +pub_glfs_h_setxattrs(struct glfs *fs, struct glfs_object *object, + const char *name, const void *value, size_t size, + int flags) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + dict_t *xattr = NULL; + void *value_cp = NULL; + + /* validate in args */ + if ((fs == NULL) || (object == NULL) || (name == NULL) || (value == NULL)) { + errno = EINVAL; + return -1; + } + + if (!name || *name == '\0') { + errno = EINVAL; + return -1; + } + + if (strlen(name) > GF_XATTR_NAME_MAX) { + errno = ENAMETOOLONG; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + value_cp = gf_memdup(value, size); + GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, + "Failed to" + " duplicate setxattr value", + out); + + xattr = dict_for_key_value(name, value_cp, size, _gf_false); + if (!xattr) { + GF_FREE(value_cp); + ret = -1; + errno = ENOMEM; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_setxattr(subvol, &loc, xattr, flags, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (xattr) + dict_unref(xattr); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_removexattrs, 3.5.1) +int +pub_glfs_h_removexattrs(struct glfs *fs, struct glfs_object *object, + const char *name) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL) || (name == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_removexattr(subvol, &loc, name, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_open, 3.4.2) +struct glfs_fd * +pub_glfs_h_open(struct glfs *fs, struct glfs_object *object, int flags) +{ + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + dict_t *fop_attr = NULL; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* check types to open */ + if (IA_ISDIR(inode->ia_type)) { + ret = -1; + errno = EISDIR; + goto out; + } + + if (!IA_ISREG(inode->ia_type)) { + ret = -1; + errno = EINVAL; + goto out; + } + + glfd = glfs_fd_new(fs); + if (!glfd) { + ret = -1; + errno = ENOMEM; + goto out; + } + + glfd->fd = fd_create(inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + glfd->fd->flags = flags; + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + ret = syncop_open(subvol, &loc, flags, glfd->fd, fop_attr, NULL); + DECODE_SYNCOP_ERR(ret); + + glfd->fd->flags = flags; + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + if (fop_attr) + dict_unref(fop_attr); + + if (ret && glfd) { + GF_REF_PUT(glfd); + glfd = NULL; + } else if (glfd) { + glfd_set_state_bind(glfd); + } + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return glfd; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_creat, 3.4.2) +struct glfs_object * +pub_glfs_h_creat(struct glfs *fs, struct glfs_object *parent, const char *path, + int flags, mode_t mode, struct stat *stat) +{ + int ret = -1; + fd_t *fd = NULL; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + struct glfs_object *object = NULL; + + /* validate in args */ + if ((fs == NULL) || (parent == NULL) || (path == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + ret = -1; + errno = ESTALE; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); + + fd = fd_create(loc.inode, getpid()); + if (!fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + fd->flags = flags; + + /* fop/op */ + ret = syncop_create(subvol, &loc, flags, mode, fd, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) { + ret = glfs_loc_link(&loc, &iatt); + if (ret != 0) { + goto out; + } + + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } + +out: + if (ret && object != NULL) { + /* Release the held reference */ + glfs_h_close(object); + object = NULL; + } + + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (xattr_req) + dict_unref(xattr_req); + + if (fd) + fd_unref(fd); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_creat_open, 6.6) +struct glfs_object * +pub_glfs_h_creat_open(struct glfs *fs, struct glfs_object *parent, + const char *path, int flags, mode_t mode, + struct stat *stat, struct glfs_fd **out_fd) +{ + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + struct glfs_object *object = NULL; + dict_t *fop_attr = NULL; + + /* validate in args */ + if ((fs == NULL) || (parent == NULL) || (path == NULL) || + (out_fd == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + ret = -1; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); + + glfd = glfs_fd_new(fs); + if (!glfd) { + ret = -1; + errno = ENOMEM; + goto out; + } + + glfd->fd = fd_create(loc.inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + glfd->fd->flags = flags; + + ret = get_fop_attr_thrd_key(&fop_attr); + if (ret) + gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); + + /* fop/op */ + ret = syncop_create(subvol, &loc, flags, mode, glfd->fd, &iatt, xattr_req, + NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) { + glfd->fd->flags = flags; + + ret = glfs_loc_link(&loc, &iatt); + if (ret != 0) { + goto out; + } + + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } + +out: + if (ret && object != NULL) { + /* Release the held reference */ + glfs_h_close(object); + object = NULL; + } + + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (fop_attr) + dict_unref(fop_attr); + + if (xattr_req) + dict_unref(xattr_req); + + if (ret && glfd) { + GF_REF_PUT(glfd); + } else if (glfd) { + glfd_set_state_bind(glfd); + *out_fd = glfd; + } + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_mkdir, 3.4.2) +struct glfs_object * +pub_glfs_h_mkdir(struct glfs *fs, struct glfs_object *parent, const char *path, + mode_t mode, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + struct glfs_object *object = NULL; + + /* validate in args */ + if ((fs == NULL) || (parent == NULL) || (path == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + errno = ESTALE; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); + + /* fop/op */ + ret = syncop_mkdir(subvol, &loc, mode, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) { + ret = glfs_loc_link(&loc, &iatt); + if (ret != 0) { + goto out; + } + + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } + +out: + if (ret && object != NULL) { + glfs_h_close(object); + object = NULL; + } + + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (xattr_req) + dict_unref(xattr_req); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_mknod, 3.4.2) +struct glfs_object * +pub_glfs_h_mknod(struct glfs *fs, struct glfs_object *parent, const char *path, + mode_t mode, dev_t dev, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + struct glfs_object *object = NULL; + + /* validate in args */ + if ((fs == NULL) || (parent == NULL) || (path == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + errno = ESTALE; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); + + /* fop/op */ + ret = syncop_mknod(subvol, &loc, mode, dev, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) { + ret = glfs_loc_link(&loc, &iatt); + if (ret != 0) { + goto out; + } + + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } +out: + if (ret && object != NULL) { + glfs_h_close(object); + object = NULL; + } + + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (xattr_req) + dict_unref(xattr_req); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_unlink, 3.4.2) +int +pub_glfs_h_unlink(struct glfs *fs, struct glfs_object *parent, const char *path) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (parent == NULL) || (path == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + errno = ESTALE; + goto out; + } + + ret = glfs_resolve_at(fs, subvol, inode, path, &loc, NULL, 0, 0); + if (ret != 0) { + goto out; + } + + if (!IA_ISDIR(loc.inode->ia_type)) { + ret = syncop_unlink(subvol, &loc, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret != 0) { + goto out; + } + } else { + ret = syncop_rmdir(subvol, &loc, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret != 0) { + goto out; + } + } + + if (ret == 0) + ret = glfs_loc_unlink(&loc); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_opendir, 3.4.2) +struct glfs_fd * +pub_glfs_h_opendir(struct glfs *fs, struct glfs_object *object) +{ + int ret = -1; + struct glfs_fd *glfd = NULL; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + if (!IA_ISDIR(inode->ia_type)) { + ret = -1; + errno = ENOTDIR; + goto out; + } + + glfd = glfs_fd_new(fs); + if (!glfd) + goto out; + + INIT_LIST_HEAD(&glfd->entries); + + glfd->fd = fd_create(inode, getpid()); + if (!glfd->fd) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (ret && glfd) { + GF_REF_PUT(glfd); + glfd = NULL; + } else if (glfd) { + glfd_set_state_bind(glfd); + } + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return glfd; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_access, 3.6.0) +int +pub_glfs_h_access(struct glfs *fs, struct glfs_object *object, int mask) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return ret; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + + ret = syncop_access(subvol, &loc, mask, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_extract_handle, 3.4.2) +ssize_t +pub_glfs_h_extract_handle(struct glfs_object *object, unsigned char *handle, + int len) +{ + ssize_t ret = -1; + + /* validate in args */ + if (object == NULL) { + errno = EINVAL; + goto out; + } + + if (!handle || !len) { + ret = GFAPI_HANDLE_LENGTH; + goto out; + } + + if (len < GFAPI_HANDLE_LENGTH) { + errno = ERANGE; + goto out; + } + + memcpy(handle, object->gfid, GFAPI_HANDLE_LENGTH); + + ret = GFAPI_HANDLE_LENGTH; + +out: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_create_from_handle, 3.4.2) +struct glfs_object * +pub_glfs_h_create_from_handle(struct glfs *fs, unsigned char *handle, int len, + struct stat *stat) +{ + loc_t loc = { + 0, + }; + int ret = -1; + struct iatt iatt = { + 0, + }; + inode_t *newinode = NULL; + xlator_t *subvol = NULL; + struct glfs_object *object = NULL; + uint64_t ctx_value = LOOKUP_NOT_NEEDED; + gf_boolean_t lookup_needed = _gf_false; + + /* validate in args */ + if ((fs == NULL) || (handle == NULL) || (len != GFAPI_HANDLE_LENGTH)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + memcpy(loc.gfid, handle, GFAPI_HANDLE_LENGTH); + + /* make sure the gfid received is valid */ + GF_VALIDATE_OR_GOTO("glfs_h_create_from_handle", + !(gf_uuid_is_null(loc.gfid)), out); + + newinode = inode_find(subvol->itable, loc.gfid); + if (newinode) { + if (!stat) /* No need of lookup */ + goto found; + + lookup_needed = inode_needs_lookup(newinode, THIS); + if (lookup_needed) { + loc.inode = newinode; + } else { + /* populate loc */ + GLFS_LOC_FILL_INODE(newinode, loc, fill_out); + + /* fop/op */ + ret = syncop_stat(subvol, &loc, &iatt, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret) { + fill_out: + /* Drop the reference hold in inode_find */ + inode_unref(newinode); + goto out; + } + + glfs_iatt_to_stat(fs, &iatt, stat); + goto found; + } + } else { + loc.inode = inode_new(subvol->itable); + if (!loc.inode) { + errno = ENOMEM; + goto out; + } + } + + ret = syncop_lookup(subvol, &loc, &iatt, 0, 0, 0); + DECODE_SYNCOP_ERR(ret); + if (ret) { + gf_smsg(subvol->name, GF_LOG_WARNING, errno, + API_MSG_INODE_REFRESH_FAILED, "gfid=%s", uuid_utoa(loc.gfid), + "error=%s", strerror(errno), NULL); + goto out; + } + + newinode = inode_link(loc.inode, 0, 0, &iatt); + if (newinode) { + if (newinode == loc.inode) { + inode_ctx_set(newinode, THIS, &ctx_value); + } + inode_lookup(newinode); + } else { + gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, + "gfid=%s", uuid_utoa(loc.gfid), NULL); + goto out; + } + + /* populate stat */ + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + +found: + object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); + if (object == NULL) { + errno = ENOMEM; + ret = -1; + goto out; + } + + /* populate the return object */ + object->inode = newinode; + gf_uuid_copy(object->gfid, object->inode->gfid); + +out: + /* TODO: Check where the inode ref is being held? */ + loc_wipe(&loc); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_close, 3.4.2) +int +pub_glfs_h_close(struct glfs_object *object) +{ + /* since glfs_h_* objects hold a reference to inode + * it is safe to keep lookup count to '0' */ + inode_forget(object->inode, 0); + inode_unref(object->inode); + GF_FREE(object); + + return 0; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_truncate, 3.4.2) +int +pub_glfs_h_truncate(struct glfs *fs, struct glfs_object *object, off_t offset) +{ + loc_t loc = { + 0, + }; + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + + DECLARE_OLD_THIS; + + /* validate in args */ + if (object == NULL) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_truncate(subvol, &loc, (off_t)offset, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) + ret = glfs_loc_unlink(&loc); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_symlink, 3.4.2) +struct glfs_object * +pub_glfs_h_symlink(struct glfs *fs, struct glfs_object *parent, + const char *name, const char *data, struct stat *stat) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + struct glfs_object *object = NULL; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((parent == NULL) || (name == NULL) || (data == NULL)) { + errno = EINVAL; + return NULL; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, parent); + if (!inode) { + errno = ESTALE; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + ret = -1; + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + ret = -1; + errno = ENOMEM; + goto out; + } + + GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, name); + + /* fop/op */ + ret = syncop_symlink(subvol, &loc, data, &iatt, xattr_req, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret == 0) { + ret = glfs_loc_link(&loc, &iatt); + if (ret != 0) { + goto out; + } + + if (stat) + glfs_iatt_to_stat(fs, &iatt, stat); + + ret = glfs_create_object(&loc, &object); + } + +out: + if (ret && object != NULL) { + pub_glfs_h_close(object); + object = NULL; + } + + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (xattr_req) + dict_unref(xattr_req); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_readlink, 3.4.2) +int +pub_glfs_h_readlink(struct glfs *fs, struct glfs_object *object, char *buf, + size_t bufsiz) +{ + loc_t loc = { + 0, + }; + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + char *linkval = NULL; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((object == NULL) || (buf == NULL)) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + GLFS_LOC_FILL_INODE(inode, loc, out); + + /* fop/op */ + ret = syncop_readlink(subvol, &loc, &linkval, bufsiz, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + /* populate out args */ + if (ret > 0) + memcpy(buf, linkval, ret); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + if (linkval) + GF_FREE(linkval); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_link, 3.4.2) +int +pub_glfs_h_link(struct glfs *fs, struct glfs_object *linksrc, + struct glfs_object *parent, const char *name) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + inode_t *pinode = NULL; + loc_t oldloc = { + 0, + }; + loc_t newloc = { + 0, + }; + struct iatt iatt = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((linksrc == NULL) || (parent == NULL) || (name == NULL)) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, linksrc); + if (!inode) { + errno = ESTALE; + goto out; + } + + if (inode->ia_type == IA_IFDIR) { + ret = -1; + errno = EISDIR; + goto out; + } + + GLFS_LOC_FILL_INODE(inode, oldloc, out); + + /* get/refresh the in arg objects inode in correlation to the xlator */ + pinode = glfs_resolve_inode(fs, subvol, parent); + if (!pinode) { + errno = ESTALE; + goto out; + } + + /* setup newloc based on parent */ + newloc.parent = inode_ref(pinode); + newloc.name = name; + ret = glfs_loc_touchup(&newloc); + if (ret != 0) { + errno = EINVAL; + goto out; + } + + /* Filling the inode of the hard link to be same as that of the + * original file + */ + newloc.inode = inode_ref(inode); + + /* fop/op */ + ret = syncop_link(subvol, &oldloc, &newloc, &iatt, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret == 0) + ret = glfs_loc_link(&newloc, &iatt); +out: + loc_wipe(&oldloc); + loc_wipe(&newloc); + + if (inode) + inode_unref(inode); + + if (pinode) + inode_unref(pinode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_rename, 3.4.2) +int +pub_glfs_h_rename(struct glfs *fs, struct glfs_object *olddir, + const char *oldname, struct glfs_object *newdir, + const char *newname) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *oldpinode = NULL; + inode_t *newpinode = NULL; + loc_t oldloc = { + 0, + }; + loc_t newloc = { + 0, + }; + struct iatt oldiatt = { + 0, + }; + struct iatt newiatt = { + 0, + }; + + DECLARE_OLD_THIS; + + /* validate in args */ + if ((olddir == NULL) || (oldname == NULL) || (newdir == NULL) || + (newname == NULL)) { + errno = EINVAL; + return -1; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + oldpinode = glfs_resolve_inode(fs, subvol, olddir); + if (!oldpinode) { + errno = ESTALE; + goto out; + } + + ret = glfs_resolve_at(fs, subvol, oldpinode, oldname, &oldloc, &oldiatt, 0, + 0); + if (ret != 0) { + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + newpinode = glfs_resolve_inode(fs, subvol, newdir); + if (!newpinode) { + errno = ESTALE; + goto out; + } + + ret = glfs_resolve_at(fs, subvol, newpinode, newname, &newloc, &newiatt, 0, + 0); + + if (ret && errno != ENOENT && newloc.parent) + goto out; + + if (newiatt.ia_type != IA_INVAL) { + if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { + /* Either both old and new must be dirs, + * or both must be non-dirs. Else, fail. + */ + ret = -1; + errno = EEXIST; + goto out; + } + } + + /* TODO: check if new or old is a prefix of the other, and fail EINVAL */ + + ret = syncop_rename(subvol, &oldloc, &newloc, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret == 0) { + inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, + newloc.parent, newloc.name, oldloc.inode, &oldiatt); + + if (newloc.inode && !inode_has_dentry(newloc.inode)) + inode_forget(newloc.inode, 0); + } + +out: + loc_wipe(&oldloc); + loc_wipe(&newloc); + + if (oldpinode) + inode_unref(oldpinode); + + if (newpinode) + inode_unref(newpinode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +/* + * Given a handle/gfid, find if the corresponding inode is present in + * the inode table. If yes create and return the corresponding glfs_object. + */ +struct glfs_object * +glfs_h_find_handle(struct glfs *fs, unsigned char *handle, int len) +{ + inode_t *newinode = NULL; + xlator_t *subvol = NULL; + struct glfs_object *object = NULL; + uuid_t gfid; + + /* validate in args */ + if ((fs == NULL) || (handle == NULL) || (len != GFAPI_HANDLE_LENGTH)) { + errno = EINVAL; + return NULL; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto out; + } + + memcpy(gfid, handle, GFAPI_HANDLE_LENGTH); + + /* make sure the gfid received is valid */ + GF_VALIDATE_OR_GOTO("glfs_h_find_handle", !(gf_uuid_is_null(gfid)), out); + + newinode = inode_find(subvol->itable, gfid); + if (!newinode) { + goto out; + } + + object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); + if (object == NULL) { + errno = ENOMEM; + goto out; + } + + /* populate the return object. The ref taken here + * is un'refed when the application does glfs_h_close() */ + object->inode = inode_ref(newinode); + gf_uuid_copy(object->gfid, object->inode->gfid); + +out: + /* inode_find takes a reference. Unref it. */ + if (newinode) + inode_unref(newinode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return object; +} + +static void +glfs_free_upcall_inode(void *to_free) +{ + struct glfs_upcall_inode *arg = to_free; + + if (!arg) + return; + + if (arg->object) + glfs_h_close(arg->object); + if (arg->p_object) + glfs_h_close(arg->p_object); + if (arg->oldp_object) + glfs_h_close(arg->oldp_object); + + GF_FREE(arg); +} + +int +glfs_h_poll_cache_invalidation(struct glfs *fs, struct glfs_upcall *up_arg, + struct gf_upcall *upcall_data) +{ + int ret = -1; + struct glfs_object *p_object = NULL; + struct glfs_object *oldp_object = NULL; + struct glfs_object *object = NULL; + struct gf_upcall_cache_invalidation *ca_data = NULL; + struct glfs_upcall_inode *up_inode_arg = NULL; + + ca_data = upcall_data->data; + GF_VALIDATE_OR_GOTO("glfs_h_poll_cache_invalidation", ca_data, out); + + object = glfs_h_find_handle(fs, upcall_data->gfid, GFAPI_HANDLE_LENGTH); + if (!object) { + /* The reason handle creation will fail is because we + * couldn't find the inode in the gfapi inode table. + * + * But since application would have taken inode_ref, the + * only case when this can happen is when it has closed + * the handle and hence will no more be interested in + * the upcall for this particular gfid. + */ + gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, + "gfid=%s", uuid_utoa(upcall_data->gfid), NULL); + errno = ESTALE; + goto out; + } + + up_inode_arg = GF_CALLOC(1, sizeof(struct glfs_upcall_inode), + glfs_mt_upcall_inode_t); + GF_VALIDATE_OR_GOTO("glfs_h_poll_cache_invalidation", up_inode_arg, out); + + up_inode_arg->object = object; + up_inode_arg->flags = ca_data->flags; + up_inode_arg->expire_time_attr = ca_data->expire_time_attr; + + /* XXX: Update stat as well in case of UP_*_TIMES. + * This will be addressed as part of INODE_UPDATE */ + if (ca_data->flags & GFAPI_INODE_UPDATE_FLAGS) { + glfs_iatt_to_stat(fs, &ca_data->stat, &up_inode_arg->buf); + } + + if (ca_data->flags & GFAPI_UP_PARENT_TIMES) { + p_object = glfs_h_find_handle(fs, ca_data->p_stat.ia_gfid, + GFAPI_HANDLE_LENGTH); + if (!p_object) { + gf_smsg(THIS->name, GF_LOG_DEBUG, errno, + API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", + uuid_utoa(ca_data->p_stat.ia_gfid), NULL); + errno = ESTALE; + goto out; + } + + glfs_iatt_to_stat(fs, &ca_data->p_stat, &up_inode_arg->p_buf); + } + up_inode_arg->p_object = p_object; + + /* In case of RENAME, update old parent as well */ + if (ca_data->flags & GFAPI_UP_RENAME) { + oldp_object = glfs_h_find_handle(fs, ca_data->oldp_stat.ia_gfid, + GFAPI_HANDLE_LENGTH); + if (!oldp_object) { + gf_smsg(THIS->name, GF_LOG_DEBUG, errno, + API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", + uuid_utoa(ca_data->oldp_stat.ia_gfid), NULL); + errno = ESTALE; + /* By the time we receive upcall old parent_dir may + * have got removed. We still need to send upcall + * for the file/dir and current parent handles. */ + up_inode_arg->oldp_object = NULL; + ret = 0; + } + + glfs_iatt_to_stat(fs, &ca_data->oldp_stat, &up_inode_arg->oldp_buf); + } + up_inode_arg->oldp_object = oldp_object; + + up_arg->reason = GLFS_UPCALL_INODE_INVALIDATE; + up_arg->event = up_inode_arg; + up_arg->free_event = glfs_free_upcall_inode; + + ret = 0; + +out: + if (ret) { + /* Close p_object and oldp_object as well if being referenced.*/ + if (object) + glfs_h_close(object); + + /* Set reason to prevent applications from using ->event */ + up_arg->reason = GLFS_UPCALL_EVENT_NULL; + GF_FREE(up_inode_arg); + } + return ret; +} + +void +glfs_release_upcall(void *ptr) +{ + struct glfs_upcall *to_free = ptr; + + if (to_free->event) + to_free->free_event(to_free->event); +} + +/* + * This API is used to poll for upcall events stored in the upcall list. + * Current users of this API is NFS-Ganesha. In case of any event received, it + * will be mapped appropriately into 'glfs_upcall' along with the handle object + * to be passed to NFS-Ganesha. + * + * On success, applications need to check if up_arg is not-NULL or errno is not + * ENOENT. glfs_upcall_get_reason() can be used to decide what kind of event + * has been received. + * + * Current supported upcall_events: + * GLFS_UPCALL_INODE_INVALIDATE + * + * After processing the event, applications need to free 'up_arg' by calling + * glfs_free(). + * + * Also similar to I/Os, the application should ideally stop polling before + * calling glfs_fini(..). Hence making an assumption that 'fs' & ctx structures + * cannot be freed while in this routine. + */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_poll_upcall, 3.7.16) +int +pub_glfs_h_poll_upcall(struct glfs *fs, struct glfs_upcall **up_arg) +{ + upcall_entry *u_list = NULL; + upcall_entry *tmp = NULL; + xlator_t *subvol = NULL; + glusterfs_ctx_t *ctx = NULL; + int ret = -1; + struct gf_upcall *upcall_data = NULL; + + DECLARE_OLD_THIS; + + if (!up_arg) { + errno = EINVAL; + goto err; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, err); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + errno = EIO; + goto restore; + } + + /* Ideally applications should stop polling before calling + * 'glfs_fini'. Yet cross check if cleanup has started. */ + pthread_mutex_lock(&fs->mutex); + { + ctx = fs->ctx; + + if (ctx->cleanup_started) { + pthread_mutex_unlock(&fs->mutex); + goto out; + } + + fs->pin_refcnt++; + + /* once we call this function, the applications seems to be + * interested in events, enable caching them */ + fs->cache_upcalls = _gf_true; + } + pthread_mutex_unlock(&fs->mutex); + + pthread_mutex_lock(&fs->upcall_list_mutex); + { + list_for_each_entry_safe(u_list, tmp, &fs->upcall_list, upcall_list) + { + list_del_init(&u_list->upcall_list); + upcall_data = &u_list->upcall_data; + break; + } + } + /* No other thread can delete this entry. So unlock it */ + pthread_mutex_unlock(&fs->upcall_list_mutex); + + if (upcall_data) { + switch (upcall_data->event_type) { + case GF_UPCALL_CACHE_INVALIDATION: + *up_arg = GLFS_CALLOC(1, sizeof(struct gf_upcall), + glfs_release_upcall, + glfs_mt_upcall_entry_t); + if (!*up_arg) { + errno = ENOMEM; + break; /* goto free u_list */ + } + + /* XXX: Need to revisit this to support + * GLFS_UPCALL_INODE_UPDATE if required. */ + ret = glfs_h_poll_cache_invalidation(fs, *up_arg, upcall_data); + if (ret || (*up_arg)->reason == GLFS_UPCALL_EVENT_NULL) { + /* It could so happen that the file which got + * upcall notification may have got deleted by + * the same client. Irrespective of the error, + * return with an error or success+ENOENT. */ + if ((*up_arg)->reason == GLFS_UPCALL_EVENT_NULL) + errno = ENOENT; + + GLFS_FREE(*up_arg); + *up_arg = NULL; + } + break; + case GF_UPCALL_RECALL_LEASE: + gf_log("glfs_h_poll_upcall", GF_LOG_DEBUG, + "UPCALL_RECALL_LEASE is not implemented yet"); + /* fallthrough till we support leases */ + case GF_UPCALL_EVENT_NULL: + /* no 'default:' label, to force handling all upcall events */ + errno = ENOENT; + break; + } + + GF_FREE(u_list->upcall_data.data); + GF_FREE(u_list); + } else { + /* fs->upcall_list was empty, no upcall events cached */ + errno = ENOENT; + } + + ret = 0; + +out: + pthread_mutex_lock(&fs->mutex); + { + fs->pin_refcnt--; + } + pthread_mutex_unlock(&fs->mutex); + + glfs_subvol_done(fs, subvol); + +restore: + __GLFS_EXIT_FS; +err: + return ret; +} + +static gf_boolean_t log_upcall370 = _gf_true; /* log once */ + +/* The old glfs_h_poll_upcall interface requires intimate knowledge of the + * structures that are returned to the calling application. This is not + * recommended, as the returned structures need to returned correctly (handles + * closed, memory free'd with the unavailable GF_FREE(), and possibly more.) + * + * To the best of our knowledge, only NFS-Ganesha uses the upcall events + * through gfapi. We keep this backwards compatibility function around so that + * applications using the existing implementation do not break. + * + * WARNING: this function will be removed in the future. + */ +GFAPI_SYMVER_PUBLIC(glfs_h_poll_upcall370, glfs_h_poll_upcall, 3.7.0) +int +pub_glfs_h_poll_upcall370(struct glfs *fs, struct glfs_callback_arg *up_arg) +{ + struct glfs_upcall *upcall = NULL; + int ret = -1; + + if (log_upcall370) { + log_upcall370 = _gf_false; + gf_log(THIS->name, GF_LOG_WARNING, + "this application is " + "compiled against an old version of libgfapi, it " + "should use glfs_free() to release the structure " + "returned by glfs_h_poll_upcall() - for more details, " + "see http://review.gluster.org/14701"); + } + + ret = pub_glfs_h_poll_upcall(fs, &upcall); + if (ret == 0) { + up_arg->fs = fs; + if ((errno == ENOENT) || !upcall || !upcall->event) { + up_arg->reason = GLFS_UPCALL_EVENT_NULL; + goto out; + } + + up_arg->reason = upcall->reason; + + if (upcall->reason == GLFS_UPCALL_INODE_INVALIDATE) { + struct glfs_callback_inode_arg *cb_inode = NULL; + struct glfs_upcall_inode *up_inode = NULL; + + cb_inode = GF_CALLOC(1, sizeof(struct glfs_callback_inode_arg), + glfs_mt_upcall_inode_t); + if (!cb_inode) { + errno = ENOMEM; + ret = -1; + goto out; + } + + up_inode = upcall->event; + + /* copy attributes one by one, the memory layout might + * be different between the old glfs_callback_inode_arg + * and new glfs_upcall_inode */ + cb_inode->object = up_inode->object; + cb_inode->flags = up_inode->flags; + memcpy(&cb_inode->buf, &up_inode->buf, sizeof(struct stat)); + cb_inode->expire_time_attr = up_inode->expire_time_attr; + cb_inode->p_object = up_inode->p_object; + memcpy(&cb_inode->p_buf, &up_inode->p_buf, sizeof(struct stat)); + cb_inode->oldp_object = up_inode->oldp_object; + memcpy(&cb_inode->oldp_buf, &up_inode->oldp_buf, + sizeof(struct stat)); + + up_arg->event_arg = cb_inode; + } + } + +out: + if (upcall) { + /* we can not use glfs_free() here, objects need to stay */ + GF_FREE(upcall->event); + GF_FREE(upcall); + } + + return ret; +} + +#ifdef HAVE_ACL_LIBACL_H +#include <glusterfs/glusterfs-acl.h> +#include <acl/libacl.h> + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_set, 3.7.0) +int +pub_glfs_h_acl_set(struct glfs *fs, struct glfs_object *object, + const acl_type_t type, const acl_t acl) +{ + int ret = -1; + char *acl_s = NULL; + const char *acl_key = NULL; + struct glfs_object *new_object = NULL; + + DECLARE_OLD_THIS; + + if (!object || !acl) { + errno = EINVAL; + return ret; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + acl_key = gf_posix_acl_get_key(type); + if (!acl_key) + goto out; + + acl_s = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE | TEXT_NUMERIC_IDS); + if (!acl_s) + goto out; + + if (IA_ISLNK(object->inode->ia_type)) { + new_object = glfs_h_resolve_symlink(fs, object); + if (new_object == NULL) + goto out; + } else + new_object = object; + + ret = pub_glfs_h_setxattrs(fs, new_object, acl_key, acl_s, + strlen(acl_s) + 1, 0); + + acl_free(acl_s); + +out: + if (IA_ISLNK(object->inode->ia_type) && new_object) + glfs_h_close(new_object); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_get, 3.7.0) +acl_t +pub_glfs_h_acl_get(struct glfs *fs, struct glfs_object *object, + const acl_type_t type) +{ + int ret = 0; + acl_t acl = NULL; + char *acl_s = NULL; + dict_t *xattr = NULL; + const char *acl_key = NULL; + struct glfs_object *new_object = NULL; + + DECLARE_OLD_THIS; + + if (!object) { + errno = EINVAL; + return NULL; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + acl_key = gf_posix_acl_get_key(type); + if (!acl_key) + goto out; + + if (IA_ISLNK(object->inode->ia_type)) { + new_object = glfs_h_resolve_symlink(fs, object); + if (new_object == NULL) + goto out; + } else + new_object = object; + + ret = glfs_h_getxattrs_common(fs, new_object, &xattr, acl_key, _gf_false); + if (ret) + goto out; + + ret = dict_get_str(xattr, (char *)acl_key, &acl_s); + if (ret) + goto out; + + acl = acl_from_text(acl_s); + +out: + if (xattr) + dict_unref(xattr); + + if (IA_ISLNK(object->inode->ia_type) && new_object) + glfs_h_close(new_object); + + __GLFS_EXIT_FS; + +invalid_fs: + return acl; +} +#else /* !HAVE_ACL_LIBACL_H */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_get, 3.7.0) +acl_t +pub_glfs_h_acl_get(struct glfs *fs, struct glfs_object *object, + const acl_type_t type) +{ + errno = ENOTSUP; + return NULL; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_set, 3.7.0) +int +pub_glfs_h_acl_set(struct glfs *fs, struct glfs_object *object, + const acl_type_t type, const acl_t acl) +{ + errno = ENOTSUP; + return -1; +} +#endif + +/* The API to perform read using anonymous fd */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_anonymous_read, 3.7.0) +ssize_t +pub_glfs_h_anonymous_read(struct glfs *fs, struct glfs_object *object, + const void *buf, size_t count, off_t offset) +{ + struct iovec iov = { + 0, + }; + ssize_t ret = 0; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + iov.iov_base = (void *)buf; + iov.iov_len = count; + + ret = glfs_anonymous_preadv(fs, object, &iov, 1, offset, 0); + + return ret; +} + +/* The API to perform write using anonymous fd */ +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_anonymous_write, 3.7.0) +ssize_t +pub_glfs_h_anonymous_write(struct glfs *fs, struct glfs_object *object, + const void *buf, size_t count, off_t offset) +{ + struct iovec iov = { + 0, + }; + ssize_t ret = 0; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + iov.iov_base = (void *)buf; + iov.iov_len = count; + + ret = glfs_anonymous_pwritev(fs, object, &iov, 1, offset, 0); + + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_object_copy, 3.11.0) +struct glfs_object * +pub_glfs_object_copy(struct glfs_object *src) +{ + struct glfs_object *object = NULL; + + GF_VALIDATE_OR_GOTO("glfs_dup_object", src, out); + + object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); + if (object == NULL) { + errno = ENOMEM; + gf_smsg(THIS->name, GF_LOG_WARNING, errno, API_MSG_CREATE_HANDLE_FAILED, + "glfs_dup_object gfid=%s", uuid_utoa(src->inode->gfid), NULL); + return NULL; + } + + object->inode = inode_ref(src->inode); + gf_uuid_copy(object->gfid, src->inode->gfid); + +out: + return object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_get_object, 3.11.0) +struct glfs_object * +pub_glfs_xreaddirplus_get_object(struct glfs_xreaddirp_stat *xstat) +{ + GF_VALIDATE_OR_GOTO("glfs_xreaddirplus_get_object", xstat, out); + + if (!(xstat->flags_handled & GFAPI_XREADDIRP_HANDLE)) + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_HANDLE_NOT_SET, + "GFAPI_XREADDIRP_HANDLE xstat=%p", xstat, "handle=%x", + xstat->flags_handled, NULL); + + return xstat->object; + +out: + return NULL; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_lease, 4.0.0) +int +pub_glfs_h_lease(struct glfs *fs, struct glfs_object *object, + struct glfs_lease *lease) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = { + 0, + }; + struct gf_lease gf_lease = { + 0, + }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* get the active volume */ + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode(fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE(inode, loc, out); + + glfs_lease_to_gf_lease(lease, &gf_lease); + + ret = syncop_lease(subvol, &loc, &gf_lease, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + gf_lease_to_glfs_lease(&gf_lease, lease); + +out: + loc_wipe(&loc); + + if (inode) + inode_unref(inode); + + glfs_subvol_done(fs, subvol); + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} diff --git a/api/src/glfs-handles.h b/api/src/glfs-handles.h new file mode 100644 index 00000000000..4d039b9c76b --- /dev/null +++ b/api/src/glfs-handles.h @@ -0,0 +1,355 @@ +/* + Copyright (c) 2013-2018 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _GLFS_HANDLES_H +#define _GLFS_HANDLES_H + +#include "glfs.h" + +/* GLFS OBJECT BASED OPERATIONS + * + * The following APIs are introduced to provide an API framework that can work + * with gluster objects (files and directories), instead of absolute paths. + * + * The following API set can be related to the POSIX *at interfaces (like + * openat (2)). The intention of these APIs is to be able to operate based + * on parent object and looking up or creating child objects within, OR to be + * used on the actual object thus looked up or created, and retrieve information + * regarding the same. + * + * The APIs also provide for generating an opaque invariant handle to the + * object, that can later be used to lookup the object, instead of the regular + * glfs_h_* variants. The APIs that provide this behaviour are, + * glfs_h_extract_handle and glfs_h_create_from_handle. + * + * The object handles can be transitioned to fd based operations as supported + * by glfs.h calls, using the glfs_h_open call. This provides a way to move + * from objects to fd's akin to moving from path to fd for required operations. + * + * NOTE: The opaque invariant handle is the GFID of the object in reality, but + * maintained as an opaque data value, for potential internal changes to the + * same without impacting the caller. + * + * NOTE: Currently looking up an object can create multiple object handles to + * the same, i.e distinct glfs_object *. Hence each such looked up or received + * handle from other calls, would need to be closed. In the future, for a given + * object these pointers would be the same, and an ease of use API to forget all + * instances of this bject would be provided (instead of a per lookup close). + * This should not change the APIs in their current form. + * + */ + +/* Handle length for object handles returned from glfs_h_extract_handle or + * glfs_h_create_from_handle */ +#define GFAPI_HANDLE_LENGTH 16 + +/* These flags should be in sync to the ones defined in upcall.h */ +#define GFAPI_UP_NLINK 0x00000001 /* update nlink */ +#define GFAPI_UP_MODE 0x00000002 /* update mode and ctime */ +#define GFAPI_UP_OWN 0x00000004 /* update mode,uid,gid and ctime */ +#define GFAPI_UP_SIZE 0x00000008 /* update fsize */ +#define GFAPI_UP_TIMES 0x00000010 /* update all times */ +#define GFAPI_UP_ATIME 0x00000020 /* update atime only */ +#define GFAPI_UP_PERM \ + 0x00000040 /* update fields needed for \ + permission checking */ +#define GFAPI_UP_RENAME \ + 0x00000080 /* this is a rename op - \ + delete the cache entry */ +#define GFAPI_UP_FORGET \ + 0x00000100 /* inode_forget on server side - \ + invalidate the cache entry */ +#define GFAPI_UP_PARENT_TIMES 0x00000200 /* update parent dir times */ + +#define GFAPI_INODE_UPDATE_FLAGS \ + (GFAPI_UP_NLINK | GFAPI_UP_MODE | GFAPI_UP_OWN | GFAPI_UP_SIZE | \ + GFAPI_UP_TIMES | GFAPI_UP_ATIME) + +/* Portability non glibc c++ build systems */ +#ifndef __THROW +#if defined __cplusplus +#define __THROW throw() +#else +#define __THROW +#endif +#endif + +__BEGIN_DECLS + +/* + * Notes: + * + * The file object handle. One per looked up, created file/directory + * + * This had been introduced to facilitate gfid/inode based gfapi + * - a requirement introduced by nfs-ganesha + */ +struct glfs_object; +typedef struct glfs_object glfs_object_t; + +/* Functions for getting details about the glfs_upcall_inode + * + * None of the pointers returned by the below functions should be free()'d, + * glfs_free()'d or glfs_h_close()'d by the application. + * + * Releasing of the structures is done by passing the glfs_upcall pointer + * to glfs_free(). + */ +struct glfs_upcall_inode; +typedef struct glfs_upcall_inode glfs_upcall_inode_t; + +glfs_object_t * +glfs_upcall_inode_get_object(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_object, 3.7.16); + +uint64_t +glfs_upcall_inode_get_flags(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_flags, 3.7.16); + +struct stat * +glfs_upcall_inode_get_stat(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_stat, 3.7.16); + +uint64_t +glfs_upcall_inode_get_expire(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_expire, 3.7.16); + +glfs_object_t * +glfs_upcall_inode_get_pobject(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_pobject, 3.7.16); + +struct stat * +glfs_upcall_inode_get_pstat(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_pstat, 3.7.16); + +glfs_object_t * +glfs_upcall_inode_get_oldpobject(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_oldpobject, 3.7.16); + +struct stat * +glfs_upcall_inode_get_oldpstat(glfs_upcall_inode_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_inode_get_oldpstat, 3.7.16); + +/* Handle based operations */ +/* Operations that generate handles */ +glfs_object_t * +glfs_h_lookupat(glfs_t *fs, glfs_object_t *parent, const char *path, + struct stat *stat, int follow) __THROW + GFAPI_PUBLIC(glfs_h_lookupat, 3.7.4); + +glfs_object_t * +glfs_h_creat(glfs_t *fs, glfs_object_t *parent, const char *path, int flags, + mode_t mode, struct stat *sb) __THROW + GFAPI_PUBLIC(glfs_h_creat, 3.4.2); + +glfs_object_t * +glfs_h_mkdir(glfs_t *fs, glfs_object_t *parent, const char *path, mode_t flags, + struct stat *sb) __THROW GFAPI_PUBLIC(glfs_h_mkdir, 3.4.2); + +glfs_object_t * +glfs_h_mknod(glfs_t *fs, glfs_object_t *parent, const char *path, mode_t mode, + dev_t dev, struct stat *sb) __THROW + GFAPI_PUBLIC(glfs_h_mknod, 3.4.2); + +glfs_object_t * +glfs_h_symlink(glfs_t *fs, glfs_object_t *parent, const char *name, + const char *data, struct stat *stat) __THROW + GFAPI_PUBLIC(glfs_h_symlink, 3.4.2); + +/* Operations on the actual objects */ +int +glfs_h_unlink(glfs_t *fs, glfs_object_t *parent, const char *path) __THROW + GFAPI_PUBLIC(glfs_h_unlink, 3.4.2); + +int +glfs_h_close(glfs_object_t *object) __THROW GFAPI_PUBLIC(glfs_h_close, 3.4.2); + +int +glfs_caller_specific_init(void *uid_caller_key, void *gid_caller_key, + void *future) __THROW + GFAPI_PUBLIC(glfs_caller_specific_init, 3.5.0); + +int +glfs_h_truncate(glfs_t *fs, glfs_object_t *object, off_t offset) __THROW + GFAPI_PUBLIC(glfs_h_truncate, 3.4.2); + +int +glfs_h_stat(glfs_t *fs, glfs_object_t *object, struct stat *stat) __THROW + GFAPI_PUBLIC(glfs_h_stat, 3.4.2); + +int +glfs_h_statfs(glfs_t *fs, glfs_object_t *object, struct statvfs *stat) __THROW + GFAPI_PUBLIC(glfs_h_statfs, 3.7.0); + +int +glfs_h_getattrs(glfs_t *fs, glfs_object_t *object, struct stat *stat) __THROW + GFAPI_PUBLIC(glfs_h_getattrs, 3.4.2); + +int +glfs_h_getxattrs(glfs_t *fs, glfs_object_t *object, const char *name, + void *value, size_t size) __THROW + GFAPI_PUBLIC(glfs_h_getxattrs, 3.5.1); + +int +glfs_h_setattrs(glfs_t *fs, glfs_object_t *object, struct stat *sb, + int valid) __THROW GFAPI_PUBLIC(glfs_h_setattrs, 3.4.2); + +int +glfs_h_setxattrs(glfs_t *fs, glfs_object_t *object, const char *name, + const void *value, size_t size, int flags) __THROW + GFAPI_PUBLIC(glfs_h_setxattrs, 3.5.0); + +int +glfs_h_readlink(glfs_t *fs, glfs_object_t *object, char *buf, + size_t bufsiz) __THROW GFAPI_PUBLIC(glfs_h_readlink, 3.4.2); + +int +glfs_h_link(glfs_t *fs, glfs_object_t *linktgt, glfs_object_t *parent, + const char *name) __THROW GFAPI_PUBLIC(glfs_h_link, 3.4.2); + +int +glfs_h_rename(glfs_t *fs, glfs_object_t *olddir, const char *oldname, + glfs_object_t *newdir, const char *newname) __THROW + GFAPI_PUBLIC(glfs_h_rename, 3.4.2); + +int +glfs_h_removexattrs(glfs_t *fs, glfs_object_t *object, const char *name) __THROW + GFAPI_PUBLIC(glfs_h_removexattrs, 3.5.1); + +/* Operations enabling opaque invariant handle to object transitions */ +ssize_t +glfs_h_extract_handle(glfs_object_t *object, unsigned char *handle, + int len) __THROW + GFAPI_PUBLIC(glfs_h_extract_handle, 3.4.2); + +/* Given a handle, looks up the inode and creates glfs_object. + * In addition, if provided 'stat', copies the inode attributes + */ +glfs_object_t * +glfs_h_create_from_handle(glfs_t *fs, unsigned char *handle, int len, + struct stat *stat) __THROW + GFAPI_PUBLIC(glfs_h_create_from_handle, 3.4.2); + +/* Operations enabling object handles to fd transitions */ +glfs_fd_t * +glfs_h_opendir(glfs_t *fs, glfs_object_t *object) __THROW + GFAPI_PUBLIC(glfs_h_opendir, 3.4.2); + +glfs_fd_t * +glfs_h_open(glfs_t *fs, glfs_object_t *object, int flags) __THROW + GFAPI_PUBLIC(glfs_h_open, 3.4.2); + +int +glfs_h_access(glfs_t *fs, glfs_object_t *object, int mask) __THROW + GFAPI_PUBLIC(glfs_h_access, 3.6.0); + +struct glfs_object * +glfs_h_creat_open(struct glfs *fs, struct glfs_object *parent, const char *path, + int flags, mode_t mode, struct stat *stat, + struct glfs_fd **out_fd) __THROW + GFAPI_PUBLIC(glfs_h_creat_open, 6.6); +/* + SYNOPSIS + + glfs_h_poll_upcall: Poll for upcall events given a 'glfs' object. + + DESCRIPTION + + This API is used to poll for upcall events stored in the + upcall list. Current users of this API is NFS-Ganesha. + In case of any event received, it will be mapped appropriately + into 'glfs_upcall' along with the handle('glfs_object') to be + passed to NFS-Ganesha. + + In case of success, applications need to check the value of + cbk->handle to be NON NULL before processing the upcall + events. + + PARAMETERS + + @fs: glfs object to poll the upcall events for + @cbk: Pointer that will contain an upcall event for use by the application. + Application is responsible for free'ing the structure with glfs_free(). + + RETURN VALUES + + 0 : Success. + -1 : Error condition, mostly due to out of memory. + +*/ + +int +glfs_h_poll_upcall(glfs_t *fs, glfs_upcall_t **cbk) __THROW + GFAPI_PUBLIC(glfs_h_poll_upcall, 3.7.16); + +int +glfs_h_acl_set(glfs_t *fs, glfs_object_t *object, const acl_type_t type, + const acl_t acl) __THROW GFAPI_PUBLIC(glfs_h_acl_set, 3.7.0); + +acl_t +glfs_h_acl_get(glfs_t *fs, glfs_object_t *object, const acl_type_t type) __THROW + GFAPI_PUBLIC(glfs_h_acl_get, 3.7.0); + +size_t +glfs_h_anonymous_write(glfs_t *fs, glfs_object_t *object, const void *buf, + size_t count, off_t offset) __THROW + GFAPI_PUBLIC(glfs_h_anonymous_write, 3.7.0); + +ssize_t +glfs_h_anonymous_read(glfs_t *fs, glfs_object_t *object, const void *buf, + size_t count, off_t offset) __THROW + GFAPI_PUBLIC(glfs_h_anonymous_read, 3.7.0); + +/* + * Caution: The object returned by this object gets freed as part + * of 'glfs_free(xstat)'. Make sure to have a copy using 'glfs_object_copy()' + * to use post that. + */ +glfs_object_t * +glfs_xreaddirplus_get_object(struct glfs_xreaddirp_stat *xstat) __THROW + GFAPI_PUBLIC(glfs_xreaddirplus_get_object, 3.11.0); + +/* Applications should close the object returned by this routine + * explicitly using 'glfs_h_close()' + */ +glfs_object_t * +glfs_object_copy(glfs_object_t *src) __THROW + GFAPI_PUBLIC(glfs_object_copy, 3.11.0); + +int +glfs_h_lease(glfs_t *fs, glfs_object_t *object, glfs_lease_t *lease) __THROW + GFAPI_PUBLIC(glfs_h_lease, 4.0.0); + +glfs_object_t * +glfs_h_find_handle(glfs_t *fs, unsigned char *handle, int len) __THROW + GFAPI_PUBLIC(glfs_h_lease, 4.0.0); + +/* Functions for getting details about the glfs_upcall_lease + * + * None of the pointers returned by the below functions should be free()'d, + * glfs_free()'d or glfs_h_close()'d by the application. + * + * Releasing of the structures is done by passing the glfs_upcall pointer + * to glfs_free(). + */ +struct glfs_upcall_lease; +typedef struct glfs_upcall_lease glfs_upcall_lease_t; + +glfs_object_t * +glfs_upcall_lease_get_object(glfs_upcall_lease_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_lease_get_object, 4.1.6); + +uint32_t +glfs_upcall_lease_get_lease_type(glfs_upcall_lease_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_lease_get_lease_type, 4.1.6); + +__END_DECLS + +#endif /* !_GLFS_HANDLES_H */ diff --git a/api/src/glfs-internal.h b/api/src/glfs-internal.h index 30ff599f2e6..7cc3b18a104 100644 --- a/api/src/glfs-internal.h +++ b/api/src/glfs-internal.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,86 +8,484 @@ cases as published by the Free Software Foundation. */ - #ifndef _GLFS_INTERNAL_H #define _GLFS_INTERNAL_H -#include "xlator.h" +#include <glusterfs/xlator.h> +#include <glusterfs/glusterfs.h> +#include <glusterfs/upcall-utils.h> +#include "glfs-handles.h" +#include <glusterfs/refcount.h> +#include <glusterfs/syncop.h> + +#define GLFS_SYMLINK_MAX_FOLLOW 2048 + +#define DEFAULT_REVAL_COUNT 1 + +/* + * According to pthread mutex and conditional variable ( cond, + * child_down_count, upcall mutex and mutex) initialization of struct glfs + * members, below GLFS_INIT_* flags are set in 'pthread_flags' member of struct + * glfs. The flags are set from glfs_init() and glfs_new_from_ctx() functions + * as part of fs inititialization. + * + * These flag bits are validated in glfs_fini() to destroy all or partially + * initialized mutex and conditional variables of glfs object. + * If you introduce new pthread mutex or conditional variable in glfs object, + * please make sure you have a flag bit intorduced here for proper cleanup + * in glfs_fini(). + * + */ + +#define PTHREAD_MUTEX_INIT(mutex, attr, flags, mask, label) \ + do { \ + int __ret = -1; \ + __ret = pthread_mutex_init(mutex, attr); \ + if (__ret == 0) \ + flags |= mask; \ + else \ + goto label; \ + } while (0) + +#define PTHREAD_MUTEX_DESTROY(mutex, flags, mask) \ + do { \ + if (flags & mask) \ + (void)pthread_mutex_destroy(mutex); \ + } while (0) + +#define PTHREAD_COND_INIT(cond, attr, flags, mask, label) \ + do { \ + int __ret = -1; \ + __ret = pthread_cond_init(cond, attr); \ + if (__ret == 0) \ + flags |= mask; \ + else \ + goto label; \ + } while (0) + +#define PTHREAD_COND_DESTROY(cond, flags, mask) \ + do { \ + if (flags & mask) \ + (void)pthread_cond_destroy(cond); \ + } while (0) + +#define GLFS_INIT_MUTEX 0x00000001 /* pthread_mutex_flag */ +#define GLFS_INIT_COND 0x00000002 /* pthread_cond_flag */ +#define GLFS_INIT_COND_CHILD 0x00000004 /* pthread_cond_child_down_flag */ +#define GLFS_INIT_MUTEX_UPCALL 0x00000008 /* pthread_mutex_upcall_flag */ + +#ifndef GF_DARWIN_HOST_OS +#ifndef GFAPI_PUBLIC +#define GFAPI_PUBLIC(sym, ver) /**/ +#endif +#ifndef GFAPI_PRIVATE +#define GFAPI_PRIVATE(sym, ver) /**/ +#endif +#if __GNUC__ >= 10 +#define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, ver) \ + __attribute__((__symver__(STR(fn) "@@GFAPI_" STR(ver)))) + +#define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, ver) \ + __attribute__((__symver__(STR(fn) "@@GFAPI_PRIVATE_" STR(ver)))) + +#define GFAPI_SYMVER_PUBLIC(fn1, fn2, ver) \ + __attribute__((__symver__(STR(fn2) "@GFAPI_" STR(ver)))) + +#define GFAPI_SYMVER_PRIVATE(fn1, fn2, ver) \ + __attribute__((__symver__(STR(fn2) "@GFAPI_PRIVATE_" STR(ver)))) + +#else +#define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, ver) \ + asm(".symver pub_" STR(fn) ", " STR(fn) "@@GFAPI_" STR(ver)); + +#define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, ver) \ + asm(".symver priv_" STR(fn) ", " STR(fn) "@@GFAPI_PRIVATE_" STR(ver)); + +#define GFAPI_SYMVER_PUBLIC(fn1, fn2, ver) \ + asm(".symver pub_" STR(fn1) ", " STR(fn2) "@GFAPI_" STR(ver)); + +#define GFAPI_SYMVER_PRIVATE(fn1, fn2, ver) \ + asm(".symver priv_" STR(fn1) ", " STR(fn2) "@GFAPI_PRIVATE_" STR(ver)); +#endif +#define STR(str) #str +#else +#ifndef GFAPI_PUBLIC +#define GFAPI_PUBLIC(sym, ver) __asm("_" __STRING(sym) "$GFAPI_" __STRING(ver)); +#endif +#ifndef GFAPI_PRIVATE +#define GFAPI_PRIVATE(sym, ver) \ + __asm("_" __STRING(sym) "$GFAPI_PRIVATE_" __STRING(ver)); +#endif +#define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, dotver) /**/ +#define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, dotver) /**/ +#define GFAPI_SYMVER_PUBLIC(fn1, fn2, dotver) /**/ +#define GFAPI_SYMVER_PRIVATE(fn1, fn2, dotver) /**/ +#endif + +#define ESTALE_RETRY(ret, errno, reval, loc, label) \ + do { \ + if (ret == -1 && errno == ESTALE) { \ + if (reval < DEFAULT_REVAL_COUNT) { \ + reval++; \ + loc_wipe(loc); \ + goto label; \ + } \ + } \ + } while (0) + +#define GLFS_LOC_FILL_INODE(oinode, loc, label) \ + do { \ + loc.inode = inode_ref(oinode); \ + gf_uuid_copy(loc.gfid, oinode->gfid); \ + ret = glfs_loc_touchup(&loc); \ + if (ret != 0) { \ + errno = EINVAL; \ + goto label; \ + } \ + } while (0) + +#define GLFS_LOC_FILL_PINODE(pinode, loc, ret, errno, label, path) \ + do { \ + loc.inode = inode_new(pinode->table); \ + if (!loc.inode) { \ + ret = -1; \ + errno = ENOMEM; \ + goto label; \ + } \ + loc.parent = inode_ref(pinode); \ + loc.name = path; \ + ret = glfs_loc_touchup(&loc); \ + if (ret != 0) { \ + errno = EINVAL; \ + goto label; \ + } \ + } while (0) struct glfs; -typedef int (*glfs_init_cbk) (struct glfs *fs, int ret); +struct _upcall_entry { + struct list_head upcall_list; + struct gf_upcall upcall_data; +}; +typedef struct _upcall_entry upcall_entry; -struct glfs { - char *volname; +typedef int (*glfs_init_cbk)(struct glfs *fs, int ret); - glusterfs_ctx_t *ctx; +struct glfs { + char *volname; + uuid_t vol_uuid; + + glusterfs_ctx_t *ctx; + + pthread_t poller; + + glfs_init_cbk init_cbk; + pthread_mutex_t mutex; + pthread_cond_t cond; + pthread_cond_t child_down_cond; /* for broadcasting CHILD_DOWN */ + int init; + int ret; + int err; + + xlator_t *active_subvol; /* active graph */ + xlator_t *mip_subvol; /* graph for which migration is in + * progress */ + xlator_t *next_subvol; /* Any new graph is put to + * next_subvol, the graph in + * next_subvol can either be moved + * to mip_subvol (if any IO picks it + * up for migration), or be + * destroyed (if there is a new + * graph, and this was never picked + * for migration) */ + xlator_t *old_subvol; + + char *oldvolfile; + ssize_t oldvollen; + + inode_t *cwd; + + uint32_t dev_id; /* Used to fill st_dev in struct stat */ + + struct list_head openfds; + + gf_boolean_t migration_in_progress; + + gf_boolean_t cache_upcalls; /* add upcalls to the upcall_list? */ + struct list_head upcall_list; + pthread_mutex_t upcall_list_mutex; /* mutex for upcall entry list */ + + uint32_t pin_refcnt; + uint32_t pthread_flags; /* GLFS_INIT_* # defines set this flag */ + + uint32_t upcall_events; /* Mask of upcall events application + * is interested in */ + glfs_upcall_cbk up_cbk; /* upcall cbk function to be registered */ + void *up_data; /* Opaque data provided by application + * during upcall registration */ + struct list_head waitq; /* waiting synctasks */ +}; - pthread_t poller; +/* This enum is used to maintain the state of glfd. In case of async fops + * fd might be closed before the actual fop is complete. Therefore we need + * to track whether the fd is closed or not, instead actually closing it.*/ +enum glfs_fd_state { GLFD_INIT, GLFD_OPEN, GLFD_CLOSE }; - glfs_init_cbk init_cbk; - pthread_mutex_t mutex; - pthread_cond_t cond; - int init; - int ret; - int err; +struct glfs_fd { + struct list_head openfds; + struct list_head list; + GF_REF_DECL; + struct glfs *fs; + enum glfs_fd_state state; + off_t offset; + fd_t *fd; /* Currently guared by @fs->mutex. TODO: per-glfd lock */ + struct list_head entries; + gf_dirent_t *next; + struct dirent *readdirbuf; + gf_lkowner_t lk_owner; + glfs_leaseid_t lease_id; /* Stores lease_id of client in glfd */ + gf_lock_t lock; /* lock taken before updating fd state */ + glfs_recall_cbk cbk; + void *cookie; +}; - xlator_t *active_subvol; - xlator_t *next_subvol; - xlator_t *old_subvol; +/* glfs object handle introduced for the alternate gfapi implementation based + on glfs handles/gfid/inode +*/ +struct glfs_object { + inode_t *inode; + uuid_t gfid; +}; - char *oldvolfile; - ssize_t oldvollen; +struct glfs_upcall { + struct glfs *fs; /* glfs object */ + enum glfs_upcall_reason reason; /* Upcall event type */ + void *event; /* changes based in the event type */ + void (*free_event)(void *); /* free event after the usage */ +}; - inode_t *cwd; +struct glfs_upcall_inode { + struct glfs_object *object; /* Object which need to be acted upon */ + int flags; /* Cache UPDATE/INVALIDATE flags */ + struct stat buf; /* Latest stat of this entry */ + unsigned int expire_time_attr; /* the amount of time for which + * the application need to cache + * this entry */ + struct glfs_object *p_object; /* parent Object to be updated */ + struct stat p_buf; /* Latest stat of parent dir handle */ + struct glfs_object *oldp_object; /* Old parent Object to be updated */ + struct stat oldp_buf; /* Latest stat of old parent dir handle */ +}; - uint32_t dev_id; /* Used to fill st_dev in struct stat */ +struct glfs_upcall_lease { + struct glfs_object *object; /* Object which need to be acted upon */ + uint32_t lease_type; /* Lease type to which client can downgrade to*/ +}; - struct list_head openfds; +struct glfs_upcall_lease_fd { + uint32_t lease_type; /* Lease type to which client can downgrade to*/ + void *fd_cookie; /* Object which need to be acted upon */ +}; - gf_boolean_t migration_in_progress; +struct glfs_xreaddirp_stat { + struct stat + st; /* Stat for that dirent - corresponds to GFAPI_XREADDIRP_STAT */ + struct glfs_object *object; /* handled for GFAPI_XREADDIRP_HANDLE */ + uint32_t flags_handled; /* final set of flags successfulyy handled */ }; -struct glfs_fd { - struct list_head openfds; - struct glfs *fs; - off_t offset; - fd_t *fd; /* Currently guared by @fs->mutex. TODO: per-glfd lock */ - struct list_head entries; - gf_dirent_t *next; +#define DEFAULT_EVENT_POOL_SIZE 16384 +#define GF_MEMPOOL_COUNT_OF_DICT_T 4096 +#define GF_MEMPOOL_COUNT_OF_DATA_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) +#define GF_MEMPOOL_COUNT_OF_DATA_PAIR_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) + +#define GF_MEMPOOL_COUNT_OF_LRU_BUF_T 256 + +typedef void(glfs_mem_release_t)(void *ptr); + +struct glfs_mem_header { + uint32_t magic; + size_t nmemb; + size_t size; + glfs_mem_release_t *release; }; -#define DEFAULT_EVENT_POOL_SIZE 16384 -#define GF_MEMPOOL_COUNT_OF_DICT_T 4096 -#define GF_MEMPOOL_COUNT_OF_DATA_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) -#define GF_MEMPOOL_COUNT_OF_DATA_PAIR_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) +#define GLFS_MEM_HEADER_SIZE (sizeof(struct glfs_mem_header)) +#define GLFS_MEM_HEADER_MAGIC 0x20170830 -int glfs_mgmt_init (struct glfs *fs); -void glfs_init_done (struct glfs *fs, int ret); -int glfs_process_volfp (struct glfs *fs, FILE *fp); -int glfs_resolve (struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, - struct iatt *iatt, int reval); -int glfs_lresolve (struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, - struct iatt *iatt, int reval); -fd_t *glfs_resolve_fd (struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); +static inline void * +__glfs_calloc(size_t nmemb, size_t size, glfs_mem_release_t release, + uint32_t type, const char *typestr) +{ + struct glfs_mem_header *header = NULL; -fd_t *__glfs_migrate_fd (struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); + header = __gf_calloc(nmemb, (size + GLFS_MEM_HEADER_SIZE), type, typestr); + if (!header) + return NULL; -int glfs_first_lookup (xlator_t *subvol); + header->magic = GLFS_MEM_HEADER_MAGIC; + header->nmemb = nmemb; + header->size = size; + header->release = release; -static inline void -__glfs_entry_fs (struct glfs *fs) + return header + 1; +} + +static inline void * +__glfs_malloc(size_t size, glfs_mem_release_t release, uint32_t type, + const char *typestr) { - THIS = fs->ctx->master; + struct glfs_mem_header *header = NULL; + + header = __gf_malloc((size + GLFS_MEM_HEADER_SIZE), type, typestr); + if (!header) + return NULL; + + header->magic = GLFS_MEM_HEADER_MAGIC; + header->nmemb = 1; + header->size = size; + header->release = release; + + return header + 1; } +static inline void * +__glfs_realloc(void *ptr, size_t size) +{ + struct glfs_mem_header *old_header = NULL; + struct glfs_mem_header *new_header = NULL; + struct glfs_mem_header tmp_header; + void *new_ptr = NULL; + + GF_ASSERT(NULL != ptr); + + old_header = (struct glfs_mem_header *)(ptr - GLFS_MEM_HEADER_SIZE); + GF_ASSERT(old_header->magic == GLFS_MEM_HEADER_MAGIC); + tmp_header = *old_header; + + new_ptr = __gf_realloc(old_header, (size + GLFS_MEM_HEADER_SIZE)); + if (!new_ptr) + return NULL; + + new_header = (struct glfs_mem_header *)new_ptr; + *new_header = tmp_header; + new_header->size = size; + + return new_header + 1; +} static inline void -__glfs_entry_fd (struct glfs_fd *fd) +__glfs_free(void *free_ptr) { - THIS = fd->fd->inode->table->xl->ctx->master; + struct glfs_mem_header *header = NULL; + void *release_ptr = NULL; + int i = 0; + + if (!free_ptr) + return; + + header = (struct glfs_mem_header *)(free_ptr - GLFS_MEM_HEADER_SIZE); + GF_ASSERT(header->magic == GLFS_MEM_HEADER_MAGIC); + + if (header->release) { + release_ptr = free_ptr; + for (i = 0; i < header->nmemb; i++) { + header->release(release_ptr); + release_ptr += header->size; + } + } + + __gf_free(header); } +#define GLFS_CALLOC(nmemb, size, release, type) \ + __glfs_calloc(nmemb, size, release, type, #type) + +#define GLFS_MALLOC(size, release, type) \ + __glfs_malloc(size, release, type, #type) + +#define GLFS_REALLOC(ptr, size) __glfs_realloc(ptr, size) + +#define GLFS_FREE(free_ptr) __glfs_free(free_ptr) + +int +glfs_mgmt_init(struct glfs *fs); +void +glfs_init_done(struct glfs *fs, int ret) GFAPI_PRIVATE(glfs_init_done, 3.4.0); +int +glfs_process_volfp(struct glfs *fs, FILE *fp); +int +glfs_resolve(struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, + struct iatt *iatt, int reval) GFAPI_PRIVATE(glfs_resolve, 3.7.0); +int +glfs_lresolve(struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, + struct iatt *iatt, int reval); +fd_t * +glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); + +fd_t * +__glfs_migrate_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); + +int +glfs_first_lookup(xlator_t *subvol); + +void +glfs_process_upcall_event(struct glfs *fs, void *data) + GFAPI_PRIVATE(glfs_process_upcall_event, 3.7.0); + +#define __GLFS_ENTRY_VALIDATE_FS(fs, label) \ + do { \ + if (!fs) { \ + errno = EINVAL; \ + goto label; \ + } \ + old_THIS = THIS; \ + THIS = fs->ctx->master; \ + } while (0) + +#define __GLFS_EXIT_FS \ + do { \ + THIS = old_THIS; \ + } while (0) + +#define __GLFS_ENTRY_VALIDATE_FD(glfd, label) \ + do { \ + if (!glfd || !glfd->fd || !glfd->fd->inode || \ + glfd->state != GLFD_OPEN) { \ + errno = EBADF; \ + goto label; \ + } \ + old_THIS = THIS; \ + THIS = glfd->fd->inode->table->xl->ctx->master; \ + } while (0) + +#define __GLFS_LOCK_WAIT(fs) \ + do { \ + struct synctask *task = NULL; \ + \ + task = synctask_get(); \ + \ + if (task) { \ + list_add_tail(&task->waitq, &fs->waitq); \ + pthread_mutex_unlock(&fs->mutex); \ + synctask_yield(task, NULL); \ + pthread_mutex_lock(&fs->mutex); \ + } else { \ + /* non-synctask */ \ + pthread_cond_wait(&fs->cond, &fs->mutex); \ + } \ + } while (0) + +#define __GLFS_SYNCTASK_WAKE(fs) \ + do { \ + struct synctask *waittask = NULL; \ + \ + while (!list_empty(&fs->waitq)) { \ + waittask = list_entry(fs->waitq.next, struct synctask, waitq); \ + list_del_init(&waittask->waitq); \ + synctask_wake(waittask); \ + } \ + } while (0) /* By default all lock attempts from user context must @@ -96,43 +494,263 @@ __glfs_entry_fd (struct glfs_fd *fd) we can give up the mutex during syncop calls so that bottom up calls (particularly CHILD_UP notify) can do a mutex_lock() on @glfs without deadlocking - the filesystem + the filesystem. + + All the fops should wait for graph migration to finish + before starting the fops. Therefore these functions should + call glfs_lock with wait_for_migration as true. But waiting + for migration to finish in call-back path can result thread + dead-locks. The reason for this is we only have finite + number of epoll threads. so if we wait on epoll threads + there will not be any thread left to handle outstanding + rpc replies. */ static inline int -glfs_lock (struct glfs *fs) +glfs_lock(struct glfs *fs, gf_boolean_t wait_for_migration) { - pthread_mutex_lock (&fs->mutex); + pthread_mutex_lock(&fs->mutex); - while (!fs->init) - pthread_cond_wait (&fs->cond, &fs->mutex); + while (!fs->init) + __GLFS_LOCK_WAIT(fs); - while (fs->migration_in_progress) - pthread_cond_wait (&fs->cond, &fs->mutex); + while (wait_for_migration && fs->migration_in_progress) + __GLFS_LOCK_WAIT(fs); - return 0; + return 0; } - static inline void -glfs_unlock (struct glfs *fs) +glfs_unlock(struct glfs *fs) { - pthread_mutex_unlock (&fs->mutex); + pthread_mutex_unlock(&fs->mutex); } +struct glfs_fd * +glfs_fd_new(struct glfs *fs); +void +glfs_fd_bind(struct glfs_fd *glfd); +void +glfd_set_state_bind(struct glfs_fd *glfd); + +xlator_t * +glfs_active_subvol(struct glfs *fs) GFAPI_PRIVATE(glfs_active_subvol, 3.4.0); +xlator_t * +__glfs_active_subvol(struct glfs *fs); +void +glfs_subvol_done(struct glfs *fs, xlator_t *subvol) + GFAPI_PRIVATE(glfs_subvol_done, 3.4.0); + +inode_t * +glfs_refresh_inode(xlator_t *subvol, inode_t *inode); + +inode_t * +glfs_cwd_get(struct glfs *fs); +int +glfs_cwd_set(struct glfs *fs, inode_t *inode); +inode_t * +glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, + struct glfs_object *object); +int +glfs_create_object(loc_t *loc, struct glfs_object **retobject); +int +__glfs_cwd_set(struct glfs *fs, inode_t *inode); + +int +glfs_resolve_base(struct glfs *fs, xlator_t *subvol, inode_t *inode, + struct iatt *iatt); + +int +glfs_resolve_at(struct glfs *fs, xlator_t *subvol, inode_t *at, + const char *origpath, loc_t *loc, struct iatt *iatt, int follow, + int reval) GFAPI_PRIVATE(glfs_resolve_at, 3.4.0); +int +glfs_loc_touchup(loc_t *loc) GFAPI_PRIVATE(glfs_loc_touchup, 3.4.0); +void +glfs_iatt_to_stat(struct glfs *fs, struct iatt *iatt, struct stat *stat); +void +glfs_iatt_from_stat(struct stat *stat, int valid, struct iatt *iatt, + int *gvalid); +int +glfs_loc_link(loc_t *loc, struct iatt *iatt); +int +glfs_loc_unlink(loc_t *loc); +int +glfs_getxattr_process(void *value, size_t size, dict_t *xattr, + const char *name); + +/* Sends RPC call to glusterd to fetch required volume info */ +int +glfs_get_volume_info(struct glfs *fs); + +/* + SYNOPSIS + + glfs_new_from_ctx: Creates a virtual mount object by taking a + glusterfs_ctx_t object. + + DESCRIPTION + + glfs_new_from_ctx() is not same as glfs_new(). It takes the + glusterfs_ctx_t object instead of creating one by glusterfs_ctx_new(). + Again the usage is restricted to NFS MOUNT over UDP i.e. in + glfs_resolve_at() which would take fs object as input but never use + (purpose is not to change the ABI of glfs_resolve_at()). + + PARAMETERS + + @ctx: glusterfs_ctx_t object + + RETURN VALUES + + fs : Pointer to the newly created glfs_t object. + NULL : Otherwise. +*/ + +struct glfs * +glfs_new_from_ctx(glusterfs_ctx_t *ctx) GFAPI_PRIVATE(glfs_new_from_ctx, 3.7.0); + +/* + SYNOPSIS + + glfs_free_from_ctx: Free up the memory occupied by glfs_t object + created by glfs_new_from_ctx(). -void glfs_fd_destroy (struct glfs_fd *glfd); + DESCRIPTION -struct glfs_fd *glfs_fd_new (struct glfs *fs); -void glfs_fd_bind (struct glfs_fd *glfd); + The glfs_t object allocated by glfs_new_from_ctx() must be released + by the caller using this routine. The usage can be found + at glfs_fini() or NFS, MOUNT over UDP i.e. + __mnt3udp_get_export_subdir_inode () + => glfs_resolve_at(). -xlator_t * glfs_active_subvol (struct glfs *fs); -xlator_t * __glfs_active_subvol (struct glfs *fs); -void glfs_subvol_done (struct glfs *fs, xlator_t *subvol); + PARAMETERS -inode_t * glfs_refresh_inode (xlator_t *subvol, inode_t *inode); + @fs: The glfs_t object to be deallocated. -inode_t *glfs_cwd_get (struct glfs *fs); -int glfs_cwd_set (struct glfs *fs, inode_t *inode); -int __glfs_cwd_set (struct glfs *fs, inode_t *inode); + RETURN VALUES + void +*/ + +void +glfs_free_from_ctx(struct glfs *fs) GFAPI_PRIVATE(glfs_free_from_ctx, 3.7.0); + +int +glfs_recall_lease_fd(struct glfs *fs, struct gf_upcall *up_data); + +int +glfs_get_upcall_cache_invalidation(struct gf_upcall *to_up_data, + struct gf_upcall *from_up_data); +int +glfs_h_poll_cache_invalidation(struct glfs *fs, struct glfs_upcall *up_arg, + struct gf_upcall *upcall_data); + +ssize_t +glfs_anonymous_preadv(struct glfs *fs, struct glfs_object *object, + const struct iovec *iovec, int iovcnt, off_t offset, + int flags); +ssize_t +glfs_anonymous_pwritev(struct glfs *fs, struct glfs_object *object, + const struct iovec *iovec, int iovcnt, off_t offset, + int flags); + +struct glfs_object * +glfs_h_resolve_symlink(struct glfs *fs, struct glfs_object *object); + +/* Deprecated structures that were passed to client applications, replaced by + * accessor functions. Do not use these in new applications, and update older + * usage. + * + * See http://review.gluster.org/14701 for more details. + * + * WARNING: These structures will be removed in the future. + */ +struct glfs_callback_arg { + struct glfs *fs; + enum glfs_upcall_reason reason; + void *event_arg; +}; + +struct glfs_callback_inode_arg { + struct glfs_object *object; /* Object which need to be acted upon */ + int flags; /* Cache UPDATE/INVALIDATE flags */ + struct stat buf; /* Latest stat of this entry */ + unsigned int expire_time_attr; /* the amount of time for which + * the application need to cache + * this entry + */ + struct glfs_object *p_object; /* parent Object to be updated */ + struct stat p_buf; /* Latest stat of parent dir handle */ + struct glfs_object *oldp_object; /* Old parent Object + * to be updated */ + struct stat oldp_buf; /* Latest stat of old parent + * dir handle */ +}; +struct dirent * +glfs_readdirbuf_get(struct glfs_fd *glfd); + +gf_dirent_t * +glfd_entry_next(struct glfs_fd *glfd, int plus); + +void +gf_dirent_to_dirent(gf_dirent_t *gf_dirent, struct dirent *dirent); + +void +gf_lease_to_glfs_lease(struct gf_lease *gf_lease, struct glfs_lease *lease); + +void +glfs_lease_to_gf_lease(struct glfs_lease *lease, struct gf_lease *gf_lease); + +void +glfs_release_upcall(void *ptr); + +int +get_fop_attr_glfd(dict_t **fop_attr, struct glfs_fd *glfd); + +int +set_fop_attr_glfd(struct glfs_fd *glfd); + +int +get_fop_attr_thrd_key(dict_t **fop_attr); + +void +unset_fop_attr(dict_t **fop_attr); + +/* + SYNOPSIS + glfs_statx: Fetch extended file attributes for the given path. + + DESCRIPTION + This function fetches extended file attributes for the given path. + + PARAMETERS + @fs: The 'virtual mount' object referencing a volume, under which file exists. + @path: Path of the file within the virtual mount. + @mask: Requested extended file attributes mask, (See mask defines above) + + RETURN VALUES + -1 : Failure. @errno will be set with the type of failure. + 0 : Filled in statxbuf with appropriate masks for valid items in the + structure. + + ERRNO VALUES + EINVAL: fs is invalid + EINVAL: mask has unsupported bits set + Other errors as returned by stat(2) + */ + +int +glfs_statx(struct glfs *fs, const char *path, unsigned int mask, + struct glfs_stat *statxbuf) GFAPI_PRIVATE(glfs_statx, 6.0); + +void +glfs_iatt_from_statx(struct iatt *, const struct glfs_stat *) + GFAPI_PRIVATE(glfs_iatt_from_statx, 6.0); + +/* + * This API is a per thread setting, similar to glfs_setfs{u/g}id, because of + * the call to syncopctx_setfspid. + */ +int +glfs_setfspid(struct glfs *, pid_t) GFAPI_PRIVATE(glfs_setfspid, 6.1); #endif /* !_GLFS_INTERNAL_H */ diff --git a/api/src/glfs-master.c b/api/src/glfs-master.c index 09a92b7f060..100dcc16cc0 100644 --- a/api/src/glfs-master.c +++ b/api/src/glfs-master.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2016 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,134 +8,176 @@ cases as published by the Free Software Foundation. */ -#include <unistd.h> -#include <string.h> -#include <stdlib.h> #include <stdio.h> -#include <inttypes.h> -#include <limits.h> -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif - -#include "xlator.h" -#include "glusterfs.h" +#include <glusterfs/glusterfs.h> #include "glfs-internal.h" - +#include "glfs-mem-types.h" +#include "gfapi-messages.h" int -glfs_graph_setup (struct glfs *fs, glusterfs_graph_t *graph) +graph_setup(struct glfs *fs, glusterfs_graph_t *graph) { - xlator_t *new_subvol = NULL; - xlator_t *old_subvol = NULL; - inode_table_t *itable = NULL; - int ret = -1; - - new_subvol = graph->top; - - /* This is called in a bottom-up context, it should specifically - NOT be glfs_lock() - */ - pthread_mutex_lock (&fs->mutex); - { - if (new_subvol->switched || - new_subvol == fs->active_subvol || - new_subvol == fs->next_subvol) { - /* Spurious CHILD_UP event on old graph */ - ret = 0; - goto unlock; - } - - if (!new_subvol->itable) { - itable = inode_table_new (131072, new_subvol); - if (!itable) { - errno = ENOMEM; - ret = -1; - goto unlock; - } - - new_subvol->itable = itable; - } - - old_subvol = fs->next_subvol; - fs->next_subvol = new_subvol; - fs->next_subvol->winds++; /* first ref */ - ret = 0; - } + xlator_t *new_subvol = NULL; + xlator_t *old_subvol = NULL; + inode_table_t *itable = NULL; + int ret = -1; + + new_subvol = graph->top; + + /* This is called in a bottom-up context, it should specifically + NOT be glfs_lock() + */ + pthread_mutex_lock(&fs->mutex); + { + if (new_subvol->switched || new_subvol == fs->active_subvol || + new_subvol == fs->next_subvol || new_subvol == fs->mip_subvol) { + /* Spurious CHILD_UP event on old graph */ + ret = 0; + goto unlock; + } + + if (!new_subvol->itable) { + itable = inode_table_new(131072, new_subvol); + if (!itable) { + errno = ENOMEM; + ret = -1; + goto unlock; + } + + new_subvol->itable = itable; + } + + old_subvol = fs->next_subvol; + fs->next_subvol = new_subvol; + fs->next_subvol->winds++; /* first ref */ + ret = 0; + } unlock: - pthread_mutex_unlock (&fs->mutex); + pthread_mutex_unlock(&fs->mutex); - if (old_subvol) - /* wasn't picked up so far, skip */ - glfs_subvol_done (fs, old_subvol); + if (old_subvol) + /* wasn't picked up so far, skip */ + glfs_subvol_done(fs, old_subvol); - return ret; + return ret; } - int -notify (xlator_t *this, int event, void *data, ...) +notify(xlator_t *this, int event, void *data, ...) { - glusterfs_graph_t *graph = NULL; - struct glfs *fs = NULL; - - graph = data; - fs = this->private; - - switch (event) { - case GF_EVENT_GRAPH_NEW: - gf_log (this->name, GF_LOG_INFO, "New graph %s (%d) coming up", - uuid_utoa ((unsigned char *)graph->graph_uuid), - graph->id); - break; - case GF_EVENT_CHILD_UP: - glfs_graph_setup (fs, graph); - glfs_init_done (fs, 0); - break; - case GF_EVENT_CHILD_DOWN: - glfs_graph_setup (fs, graph); - glfs_init_done (fs, 1); - break; - case GF_EVENT_CHILD_CONNECTING: - break; - default: - gf_log (this->name, GF_LOG_DEBUG, - "got notify event %d", event); - break; - } - - return 0; + glusterfs_graph_t *graph = NULL; + struct glfs *fs = NULL; + + graph = data; + fs = this->private; + + switch (event) { + case GF_EVENT_GRAPH_NEW: + gf_smsg(this->name, GF_LOG_INFO, 0, API_MSG_NEW_GRAPH, + "graph-uuid=%s", + uuid_utoa((unsigned char *)graph->graph_uuid), "id=%d", + graph->id, NULL); + break; + case GF_EVENT_CHILD_UP: + pthread_mutex_lock(&fs->mutex); + { + graph->used = 1; + } + pthread_mutex_unlock(&fs->mutex); + graph_setup(fs, graph); + glfs_init_done(fs, 0); + break; + case GF_EVENT_CHILD_DOWN: + pthread_mutex_lock(&fs->mutex); + { + graph->used = 0; + pthread_cond_broadcast(&fs->child_down_cond); + } + pthread_mutex_unlock(&fs->mutex); + glfs_init_done(fs, 1); + break; + case GF_EVENT_CHILD_CONNECTING: + break; + case GF_EVENT_UPCALL: + glfs_process_upcall_event(fs, data); + break; + default: + gf_msg_debug(this->name, 0, "got notify event %d", event); + break; + } + + return 0; } - int -mem_acct_init (xlator_t *this) +mem_acct_init(xlator_t *this) { - return 0; -} + int ret = -1; + + if (!this) + return ret; + ret = xlator_mem_acct_init(this, glfs_mt_end + 1); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, ENOMEM, API_MSG_MEM_ACCT_INIT_FAILED, + NULL); + return ret; + } + + return 0; +} int -init (xlator_t *this) +init(xlator_t *this) { - return 0; + return 0; } - void -fini (xlator_t *this) +fini(xlator_t *this) { +} +/* place-holder fops */ +int +glfs_forget(xlator_t *this, inode_t *inode) +{ + return 0; } +int +glfs_release(xlator_t *this, fd_t *fd) +{ + return 0; +} -struct xlator_dumpops dumpops; +int +glfs_releasedir(xlator_t *this, fd_t *fd) +{ + return 0; +} +struct xlator_dumpops dumpops; struct xlator_fops fops; - -struct xlator_cbks cbks; +struct xlator_cbks cbks = { + .forget = glfs_forget, + .release = glfs_release, + .releasedir = glfs_releasedir, +}; + +xlator_api_t xlator_api = { + .init = init, + .fini = fini, + .notify = notify, + .mem_acct_init = mem_acct_init, + .op_version = {1}, + .dumpops = &dumpops, + .fops = &fops, + .cbks = &cbks, + .identifier = "glfs-api", + .category = GF_MAINTAINED, +}; diff --git a/api/src/glfs-mem-types.h b/api/src/glfs-mem-types.h index 590acd03f11..bfa325a3ad9 100644 --- a/api/src/glfs-mem-types.h +++ b/api/src/glfs-mem-types.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2017 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -11,20 +11,25 @@ #ifndef _GLFS_MEM_TYPES_H #define _GLFS_MEM_TYPES_H -#include "mem-types.h" +#include <glusterfs/mem-types.h> #define GF_MEM_TYPE_START (gf_common_mt_end + 1) enum glfs_mem_types_ { - glfs_mt_glfs_t, - glfs_mt_call_pool_t, - glfs_mt_xlator_t, - glfs_mt_glfs_fd_t, - glfs_mt_glfs_io_t, - glfs_mt_volfile_t, - glfs_mt_xlator_cmdline_option_t, - glfs_mt_end - + glfs_mt_call_pool_t = GF_MEM_TYPE_START, + glfs_mt_xlator_t, + glfs_mt_glfs_fd_t, + glfs_mt_glfs_io_t, + glfs_mt_volfile_t, + glfs_mt_xlator_cmdline_option_t, + glfs_mt_server_cmdline_t, + glfs_mt_glfs_object_t, + glfs_mt_readdirbuf_t, + glfs_mt_upcall_entry_t, + glfs_mt_acl_t, + glfs_mt_upcall_inode_t, + glfs_mt_realpath_t, + glfs_mt_xreaddirp_stat_t, + glfs_mt_end }; #endif - diff --git a/api/src/glfs-mgmt.c b/api/src/glfs-mgmt.c index a76692bfd06..7c82b8cd162 100644 --- a/api/src/glfs-mgmt.c +++ b/api/src/glfs-mgmt.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,7 +8,6 @@ cases as published by the Free Software Foundation. */ - #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> @@ -16,676 +15,1035 @@ #include <signal.h> #include <pthread.h> -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif /* _CONFIG_H */ - -#include "glusterfs.h" -#include "stack.h" -#include "dict.h" -#include "event.h" -#include "defaults.h" +#include <glusterfs/glusterfs.h> +#include "glfs.h" +#include <glusterfs/dict.h> #include "rpc-clnt.h" #include "protocol-common.h" -#include "glusterfs3.h" -#include "portmap-xdr.h" #include "xdr-generic.h" +#include "rpc-common-xdr.h" -#include "syncop.h" -#include "xlator.h" +#include <glusterfs/syncop.h> #include "glfs-internal.h" -#include "glfs-mem-types.h" - +#include "gfapi-messages.h" +#include <glusterfs/syscall.h> -int glfs_volfile_fetch (struct glfs *fs); +int +glfs_volfile_fetch(struct glfs *fs); +int32_t +glfs_get_volume_info_rpc(call_frame_t *frame, xlator_t *this, struct glfs *fs); int -glfs_process_volfp (struct glfs *fs, FILE *fp) +glfs_process_volfp(struct glfs *fs, FILE *fp) { - glusterfs_graph_t *graph = NULL; - int ret = -1; - xlator_t *trav = NULL; - glusterfs_ctx_t *ctx = NULL; - - ctx = fs->ctx; - graph = glusterfs_graph_construct (fp); - if (!graph) { - gf_log ("glfs", GF_LOG_ERROR, "failed to construct the graph"); - goto out; - } - - for (trav = graph->first; trav; trav = trav->next) { - if (strcmp (trav->type, "mount/fuse") == 0) { - gf_log ("glfs", GF_LOG_ERROR, - "fuse xlator cannot be specified " - "in volume file"); - goto out; - } - } - - ret = glusterfs_graph_prepare (graph, ctx); - if (ret) { - glusterfs_graph_destroy (graph); - goto out; - } - - ret = glusterfs_graph_activate (graph, ctx); - - if (ret) { - glusterfs_graph_destroy (graph); - goto out; - } - - ret = 0; + glusterfs_graph_t *graph = NULL; + int ret = -1; + xlator_t *trav = NULL; + glusterfs_ctx_t *ctx = NULL; + + ctx = fs->ctx; + graph = glusterfs_graph_construct(fp); + if (!graph) { + gf_smsg("glfs", GF_LOG_ERROR, errno, API_MSG_GRAPH_CONSTRUCT_FAILED, + NULL); + goto out; + } + + for (trav = graph->first; trav; trav = trav->next) { + if (strcmp(trav->type, "mount/api") == 0) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_API_XLATOR_ERROR, + NULL); + goto out; + } + } + + ret = glusterfs_graph_prepare(graph, ctx, fs->volname); + if (ret) { + glusterfs_graph_destroy(graph); + goto out; + } + + ret = glusterfs_graph_activate(graph, ctx); + + if (ret) { + glusterfs_graph_destroy(graph); + goto out; + } + + gf_log_dump_graph(fp, graph); + + ret = 0; out: - if (fp) - fclose (fp); + if (fp) + fclose(fp); - if (!ctx->active) { - ret = -1; - } + if (!ctx->active) { + ret = -1; + } - return ret; + return ret; } - int -mgmt_cbk_spec (struct rpc_clnt *rpc, void *mydata, void *data) +mgmt_cbk_spec(struct rpc_clnt *rpc, void *mydata, void *data) { - struct glfs *fs = NULL; - xlator_t *this = NULL; + struct glfs *fs = NULL; + xlator_t *this = NULL; - this = mydata; - fs = this->private; + this = mydata; + fs = this->private; - glfs_volfile_fetch (fs); + glfs_volfile_fetch(fs); - return 0; + return 0; } - int -mgmt_cbk_event (struct rpc_clnt *rpc, void *mydata, void *data) +mgmt_cbk_event(struct rpc_clnt *rpc, void *mydata, void *data) { - return 0; + return 0; } +static int +mgmt_cbk_statedump(struct rpc_clnt *rpc, void *mydata, void *data) +{ + struct glfs *fs = NULL; + xlator_t *this = NULL; + gf_statedump target_pid = { + 0, + }; + struct iovec *iov = NULL; + int ret = -1; + + this = mydata; + if (!this) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "mydata", NULL); + errno = EINVAL; + goto out; + } + + fs = this->private; + if (!fs) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "glfs", NULL); + errno = EINVAL; + goto out; + } + + iov = (struct iovec *)data; + if (!iov) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "iovec data", NULL); + errno = EINVAL; + goto out; + } + + ret = xdr_to_generic(*iov, &target_pid, (xdrproc_t)xdr_gf_statedump); + if (ret < 0) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_DECODE_XDR_FAILED, NULL); + goto out; + } + + gf_msg_trace("glfs", 0, "statedump requested for pid: %d", target_pid.pid); + + if ((uint64_t)getpid() == target_pid.pid) { + gf_msg_debug("glfs", 0, "Taking statedump for pid: %d", target_pid.pid); + + ret = glfs_sysrq(fs, GLFS_SYSRQ_STATEDUMP); + if (ret < 0) { + gf_smsg("glfs", GF_LOG_INFO, 0, API_MSG_STATEDUMP_FAILED, NULL); + } + } +out: + return ret; +} -rpcclnt_cb_actor_t gluster_cbk_actors[] = { - [GF_CBK_FETCHSPEC] = {"FETCHSPEC", GF_CBK_FETCHSPEC, mgmt_cbk_spec }, - [GF_CBK_EVENT_NOTIFY] = {"EVENTNOTIFY", GF_CBK_EVENT_NOTIFY, - mgmt_cbk_event}, +static rpcclnt_cb_actor_t mgmt_cbk_actors[GF_CBK_MAXVALUE] = { + [GF_CBK_FETCHSPEC] = {"FETCHSPEC", mgmt_cbk_spec, GF_CBK_FETCHSPEC}, + [GF_CBK_EVENT_NOTIFY] = {"EVENTNOTIFY", mgmt_cbk_event, + GF_CBK_EVENT_NOTIFY}, + [GF_CBK_STATEDUMP] = {"STATEDUMP", mgmt_cbk_statedump, GF_CBK_STATEDUMP}, }; - -struct rpcclnt_cb_program mgmt_cbk_prog = { - .progname = "GlusterFS Callback", - .prognum = GLUSTER_CBK_PROGRAM, - .progver = GLUSTER_CBK_VERSION, - .actors = gluster_cbk_actors, - .numactors = GF_CBK_MAXVALUE, +static struct rpcclnt_cb_program mgmt_cbk_prog = { + .progname = "GlusterFS Callback", + .prognum = GLUSTER_CBK_PROGRAM, + .progver = GLUSTER_CBK_VERSION, + .actors = mgmt_cbk_actors, + .numactors = GF_CBK_MAXVALUE, }; -char *clnt_handshake_procs[GF_HNDSK_MAXVALUE] = { - [GF_HNDSK_NULL] = "NULL", - [GF_HNDSK_SETVOLUME] = "SETVOLUME", - [GF_HNDSK_GETSPEC] = "GETSPEC", - [GF_HNDSK_PING] = "PING", - [GF_HNDSK_EVENT_NOTIFY] = "EVENTNOTIFY", +static char *clnt_handshake_procs[GF_HNDSK_MAXVALUE] = { + [GF_HNDSK_NULL] = "NULL", + [GF_HNDSK_SETVOLUME] = "SETVOLUME", + [GF_HNDSK_GETSPEC] = "GETSPEC", + [GF_HNDSK_PING] = "PING", + [GF_HNDSK_EVENT_NOTIFY] = "EVENTNOTIFY", + [GF_HNDSK_GET_VOLUME_INFO] = "GETVOLUMEINFO", }; -rpc_clnt_prog_t clnt_handshake_prog = { - .progname = "GlusterFS Handshake", - .prognum = GLUSTER_HNDSK_PROGRAM, - .progver = GLUSTER_HNDSK_VERSION, - .procnames = clnt_handshake_procs, +static rpc_clnt_prog_t clnt_handshake_prog = { + .progname = "GlusterFS Handshake", + .prognum = GLUSTER_HNDSK_PROGRAM, + .progver = GLUSTER_HNDSK_VERSION, + .procnames = clnt_handshake_procs, }; - int -mgmt_submit_request (void *req, call_frame_t *frame, - glusterfs_ctx_t *ctx, - rpc_clnt_prog_t *prog, int procnum, - fop_cbk_fn_t cbkfn, xdrproc_t xdrproc) +mgmt_submit_request(void *req, call_frame_t *frame, glusterfs_ctx_t *ctx, + rpc_clnt_prog_t *prog, int procnum, fop_cbk_fn_t cbkfn, + xdrproc_t xdrproc) { - int ret = -1; - int count = 0; - struct iovec iov = {0, }; - struct iobuf *iobuf = NULL; - struct iobref *iobref = NULL; - ssize_t xdr_size = 0; - - iobref = iobref_new (); - if (!iobref) { - goto out; - } - - if (req) { - xdr_size = xdr_sizeof (xdrproc, req); - - iobuf = iobuf_get2 (ctx->iobuf_pool, xdr_size); - if (!iobuf) { - goto out; - }; - - iobref_add (iobref, iobuf); - - iov.iov_base = iobuf->ptr; - iov.iov_len = iobuf_pagesize (iobuf); - - /* Create the xdr payload */ - ret = xdr_serialize_generic (iov, req, xdrproc); - if (ret == -1) { - gf_log (THIS->name, GF_LOG_WARNING, - "failed to create XDR payload"); - goto out; - } - iov.iov_len = ret; - count = 1; - } - - /* Send the msg */ - ret = rpc_clnt_submit (ctx->mgmt, prog, procnum, cbkfn, - &iov, count, - NULL, 0, iobref, frame, NULL, 0, NULL, 0, NULL); + int ret = -1; + int count = 0; + struct iovec iov = { + 0, + }; + struct iobuf *iobuf = NULL; + struct iobref *iobref = NULL; + ssize_t xdr_size = 0; + + iobref = iobref_new(); + if (!iobref) { + goto out; + } + + if (req) { + xdr_size = xdr_sizeof(xdrproc, req); + + iobuf = iobuf_get2(ctx->iobuf_pool, xdr_size); + if (!iobuf) { + goto out; + }; + + iobref_add(iobref, iobuf); + + iov.iov_base = iobuf->ptr; + iov.iov_len = iobuf_pagesize(iobuf); + + /* Create the xdr payload */ + ret = xdr_serialize_generic(iov, req, xdrproc); + if (ret == -1) { + gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_XDR_PAYLOAD_FAILED, + NULL); + goto out; + } + iov.iov_len = ret; + count = 1; + } + + /* Send the msg */ + ret = rpc_clnt_submit(ctx->mgmt, prog, procnum, cbkfn, &iov, count, NULL, 0, + iobref, frame, NULL, 0, NULL, 0, NULL); out: - if (iobref) - iobref_unref (iobref); + if (iobref) + iobref_unref(iobref); - if (iobuf) - iobuf_unref (iobuf); - return ret; + if (iobuf) + iobuf_unref(iobuf); + return ret; } - -static int -xlator_equal_rec (xlator_t *xl1, xlator_t *xl2) +/* + * Callback routine for 'GF_HNDSK_GET_VOLUME_INFO' rpc request + */ +int +mgmt_get_volinfo_cbk(struct rpc_req *req, struct iovec *iov, int count, + void *myframe) { - xlator_list_t *trav1 = NULL; - xlator_list_t *trav2 = NULL; - int ret = 0; - - if (xl1 == NULL || xl2 == NULL) { - gf_log ("xlator", GF_LOG_DEBUG, "invalid argument"); - return -1; - } - - trav1 = xl1->children; - trav2 = xl2->children; - - while (trav1 && trav2) { - ret = xlator_equal_rec (trav1->xlator, trav2->xlator); - if (ret) { - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "xlators children not equal"); - goto out; - } - - trav1 = trav1->next; - trav2 = trav2->next; - } - - if (trav1 || trav2) { - ret = -1; - goto out; - } - - if (strcmp (xl1->name, xl2->name)) { - ret = -1; - goto out; - } -out : - return ret; -} + int ret = 0; + char *volume_id_str = NULL; + dict_t *dict = NULL; + gf_get_volume_info_rsp rsp = { + 0, + }; + call_frame_t *frame = NULL; + glusterfs_ctx_t *ctx = NULL; + struct glfs *fs = NULL; + struct syncargs *args; + + frame = myframe; + ctx = frame->this->ctx; + args = frame->local; + + if (!ctx) { + gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_NULL, + "context", NULL); + errno = EINVAL; + ret = -1; + goto out; + } + + fs = ((xlator_t *)ctx->master)->private; + + if (-1 == req->rpc_status) { + gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, + API_MSG_CALL_NOT_SUCCESSFUL, NULL); + errno = EINVAL; + ret = -1; + goto out; + } + + ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_get_volume_info_rsp); + + if (ret < 0) { + gf_smsg(frame->this->name, GF_LOG_ERROR, 0, + API_MSG_XDR_RESPONSE_DECODE_FAILED, NULL); + goto out; + } + + gf_msg_debug(frame->this->name, 0, + "Received resp to GET_VOLUME_INFO " + "RPC: %d", + rsp.op_ret); + + if (rsp.op_ret == -1) { + errno = rsp.op_errno; + ret = -1; + goto out; + } + + if (!rsp.dict.dict_len) { + gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_CALL_NOT_VALID, + NULL); + ret = -1; + errno = EINVAL; + goto out; + } + + dict = dict_new(); + + if (!dict) { + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = dict_unserialize(rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) { + errno = ENOMEM; + goto out; + } + + ret = dict_get_str_sizen(dict, "volume_id", &volume_id_str); + if (ret) { + errno = EINVAL; + goto out; + } + + ret = 0; +out: + if (volume_id_str) { + gf_msg_debug(frame->this->name, 0, "Volume Id: %s", volume_id_str); + pthread_mutex_lock(&fs->mutex); + gf_uuid_parse(volume_id_str, fs->vol_uuid); + pthread_mutex_unlock(&fs->mutex); + } + if (ret) { + gf_smsg(frame->this->name, GF_LOG_ERROR, errno, + API_MSG_GET_VOLINFO_CBK_FAILED, "error=%s", strerror(errno), + NULL); + } -static gf_boolean_t -is_graph_topology_equal (glusterfs_graph_t *graph1, - glusterfs_graph_t *graph2) -{ - xlator_t *trav1 = NULL; - xlator_t *trav2 = NULL; - gf_boolean_t ret = _gf_true; + if (dict) + dict_unref(dict); - trav1 = graph1->first; - trav2 = graph2->first; + if (rsp.dict.dict_val) + free(rsp.dict.dict_val); - ret = xlator_equal_rec (trav1, trav2); + if (rsp.op_errstr) + free(rsp.op_errstr); - if (ret) { - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "graphs are not equal"); - ret = _gf_false; - goto out; - } + gf_msg_debug(frame->this->name, 0, "Returning: %d", ret); - ret = _gf_true; - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "graphs are equal"); + __wake(args); -out: - return ret; + return ret; } - -/* Function has 3types of return value 0, -ve , 1 - * return 0 =======> reconfiguration of options has succeeded - * return 1 =======> the graph has to be reconstructed and all the xlators should be inited - * return -1(or -ve) =======> Some Internal Error occurred during the operation - */ -static int -glusterfs_volfile_reconfigure (struct glfs *fs, FILE *newvolfile_fp) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_get_volumeid, 3.5.0) +int +pub_glfs_get_volumeid(struct glfs *fs, char *volid, size_t size) { - glusterfs_graph_t *oldvolfile_graph = NULL; - glusterfs_graph_t *newvolfile_graph = NULL; - FILE *oldvolfile_fp = NULL; - glusterfs_ctx_t *ctx = NULL; - - int ret = -1; - - oldvolfile_fp = tmpfile (); - if (!oldvolfile_fp) - goto out; - - if (!fs->oldvollen) { - ret = 1; // Has to call INIT for the whole graph - goto out; - } - fwrite (fs->oldvolfile, fs->oldvollen, 1, oldvolfile_fp); - fflush (oldvolfile_fp); - if (ferror (oldvolfile_fp)) { - goto out; - } - - oldvolfile_graph = glusterfs_graph_construct (oldvolfile_fp); - if (!oldvolfile_graph) { - goto out; - } - - newvolfile_graph = glusterfs_graph_construct (newvolfile_fp); - if (!newvolfile_graph) { - goto out; - } - - if (!is_graph_topology_equal (oldvolfile_graph, - newvolfile_graph)) { - - ret = 1; - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "Graph topology not equal(should call INIT)"); - goto out; - } - - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "Only options have changed in the new " - "graph"); - - ctx = fs->ctx; - - if (!ctx) { - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "glusterfs_ctx_get() returned NULL"); - goto out; - } - - oldvolfile_graph = ctx->active; - - if (!oldvolfile_graph) { - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "glusterfs_ctx->active is NULL"); - goto out; - } - - /* */ - ret = glusterfs_graph_reconfigure (oldvolfile_graph, - newvolfile_graph); - if (ret) { - gf_log ("glfs-mgmt", GF_LOG_DEBUG, - "Could not reconfigure new options in old graph"); - goto out; - } - - ret = 0; -out: - if (oldvolfile_fp) - fclose (oldvolfile_fp); + /* TODO: Define a global macro to store UUID size */ + size_t uuid_size = 16; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + pthread_mutex_lock(&fs->mutex); + { + /* check if the volume uuid is initialized */ + if (!gf_uuid_is_null(fs->vol_uuid)) { + pthread_mutex_unlock(&fs->mutex); + goto done; + } + } + pthread_mutex_unlock(&fs->mutex); - return ret; -} + /* Need to fetch volume_uuid */ + glfs_get_volume_info(fs); + if (gf_uuid_is_null(fs->vol_uuid)) { + gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_FETCH_VOLUUID_FAILED, + NULL); + goto out; + } -static int -glusterfs_oldvolfile_update (struct glfs *fs, char *volfile, ssize_t size) -{ - int ret = -1; - - fs->oldvollen = size; - if (!fs->oldvolfile) { - fs->oldvolfile = GF_CALLOC (1, size+1, glfs_mt_volfile_t); - } else { - fs->oldvolfile = GF_REALLOC (fs->oldvolfile, size+1); - } - - if (!fs->oldvolfile) { - fs->oldvollen = 0; - } else { - memcpy (fs->oldvolfile, volfile, size); - fs->oldvollen = size; - ret = 0; - } - - return ret; -} +done: + if (!volid || !size) { + gf_msg_debug(THIS->name, 0, "volumeid/size is null"); + __GLFS_EXIT_FS; + return uuid_size; + } + + if (size < uuid_size) { + gf_smsg(THIS->name, GF_LOG_ERROR, ERANGE, API_MSG_INSUFF_SIZE, NULL); + errno = ERANGE; + goto out; + } + + memcpy(volid, fs->vol_uuid, uuid_size); + + __GLFS_EXIT_FS; + + return uuid_size; +out: + __GLFS_EXIT_FS; + +invalid_fs: + return -1; +} int -mgmt_getspec_cbk (struct rpc_req *req, struct iovec *iov, int count, - void *myframe) +glfs_get_volume_info(struct glfs *fs) { - gf_getspec_rsp rsp = {0,}; - call_frame_t *frame = NULL; - glusterfs_ctx_t *ctx = NULL; - int ret = 0; - ssize_t size = 0; - FILE *tmpfp = NULL; - int need_retry = 0; - struct glfs *fs = NULL; - - frame = myframe; - ctx = frame->this->ctx; - fs = ((xlator_t *)ctx->master)->private; - - if (-1 == req->rpc_status) { - ret = -1; - need_retry = 1; - goto out; - } - - ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_getspec_rsp); - if (ret < 0) { - gf_log (frame->this->name, GF_LOG_ERROR, "XDR decoding error"); - ret = -1; - goto out; - } - - if (-1 == rsp.op_ret) { - gf_log (frame->this->name, GF_LOG_ERROR, - "failed to get the 'volume file' from server"); - ret = -1; - goto out; - } - - ret = 0; - size = rsp.op_ret; - - if ((size == fs->oldvollen) && - (memcmp (fs->oldvolfile, rsp.spec, size) == 0)) { - gf_log (frame->this->name, GF_LOG_INFO, - "No change in volfile, continuing"); - goto out; - } - - tmpfp = tmpfile (); - if (!tmpfp) { - ret = -1; - goto out; - } - - fwrite (rsp.spec, size, 1, tmpfp); - fflush (tmpfp); - if (ferror (tmpfp)) { - ret = -1; - goto out; - } - - /* Check if only options have changed. No need to reload the - * volfile if topology hasn't changed. - * glusterfs_volfile_reconfigure returns 3 possible return states - * return 0 =======> reconfiguration of options has succeeded - * return 1 =======> the graph has to be reconstructed and all the xlators should be inited - * return -1(or -ve) =======> Some Internal Error occurred during the operation - */ - - ret = glusterfs_volfile_reconfigure (fs, tmpfp); - if (ret == 0) { - gf_log ("glusterfsd-mgmt", GF_LOG_DEBUG, - "No need to re-load volfile, reconfigure done"); - ret = glusterfs_oldvolfile_update (fs, rsp.spec, size); - goto out; - } - - if (ret < 0) { - gf_log ("glusterfsd-mgmt", GF_LOG_DEBUG, - "Reconfigure failed !!"); - goto out; - } - - ret = glfs_process_volfp (fs, tmpfp); - /* tmpfp closed */ - tmpfp = NULL; - if (ret) - goto out; - - ret = glusterfs_oldvolfile_update (fs, rsp.spec, size); -out: - STACK_DESTROY (frame->root); + call_frame_t *frame = NULL; + glusterfs_ctx_t *ctx = NULL; + struct syncargs args = { + 0, + }; + int ret = 0; - if (rsp.spec) - free (rsp.spec); + ctx = fs->ctx; + frame = create_frame(THIS, ctx->pool); + if (!frame) { + gf_smsg("glfs", GF_LOG_ERROR, ENOMEM, API_MSG_FRAME_CREAT_FAILED, NULL); + ret = -1; + goto out; + } - // Stop if server is running at an unsupported op-version - if (ENOTSUP == ret) { - gf_log ("mgmt", GF_LOG_ERROR, "Server is operating at an " - "op-version which is not supported"); - glfs_init_done (fs, -1); - } + frame->local = &args; - if (ret && ctx && !ctx->active) { - /* Do it only for the first time */ - /* Failed to get the volume file, something wrong, - restart the process */ - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "failed to fetch volume file (key:%s)", - ctx->cmd_args.volfile_id); - if (!need_retry) - glfs_init_done (fs, -1); - } - - if (tmpfp) - fclose (tmpfp); - - return 0; + __yawn((&args)); + + ret = glfs_get_volume_info_rpc(frame, THIS, fs); + if (ret) + goto out; + + __yield((&args)); + + frame->local = NULL; + STACK_DESTROY(frame->root); + +out: + return ret; } +int32_t +glfs_get_volume_info_rpc(call_frame_t *frame, xlator_t *this, struct glfs *fs) +{ + gf_get_volume_info_req req = {{ + 0, + }}; + int ret = 0; + glusterfs_ctx_t *ctx = NULL; + dict_t *dict = NULL; + int32_t flags = 0; + + if (!frame || !this || !fs) { + ret = -1; + goto out; + } + + ctx = fs->ctx; + + dict = dict_new(); + if (!dict) { + ret = -1; + goto out; + } + + if (fs->volname) { + ret = dict_set_str(dict, "volname", fs->volname); + if (ret) + goto out; + } + + // Set the flags for the fields which we are interested in + flags = (int32_t)GF_GET_VOLUME_UUID; // ctx->flags; + ret = dict_set_int32(dict, "flags", flags); + if (ret) { + gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, + API_MSG_DICT_SET_FAILED, "flags", NULL); + goto out; + } + + ret = dict_allocate_and_serialize(dict, &req.dict.dict_val, + &req.dict.dict_len); + + ret = mgmt_submit_request(&req, frame, ctx, &clnt_handshake_prog, + GF_HNDSK_GET_VOLUME_INFO, mgmt_get_volinfo_cbk, + (xdrproc_t)xdr_gf_get_volume_info_req); +out: + if (dict) { + dict_unref(dict); + } -int -glfs_volfile_fetch (struct glfs *fs) + GF_FREE(req.dict.dict_val); + + return ret; +} + +static int +glusterfs_oldvolfile_update(struct glfs *fs, char *volfile, ssize_t size) { - cmd_args_t *cmd_args = NULL; - gf_getspec_req req = {0, }; - int ret = 0; - call_frame_t *frame = NULL; - glusterfs_ctx_t *ctx = NULL; - dict_t *dict = NULL; + int ret = -1; - ctx = fs->ctx; - cmd_args = &ctx->cmd_args; + pthread_mutex_lock(&fs->mutex); - frame = create_frame (THIS, ctx->pool); + fs->oldvollen = size; + if (!fs->oldvolfile) { + fs->oldvolfile = CALLOC(1, size + 1); + } else { + fs->oldvolfile = REALLOC(fs->oldvolfile, size + 1); + } - req.key = cmd_args->volfile_id; - req.flags = 0; + if (!fs->oldvolfile) { + fs->oldvollen = 0; + } else { + memcpy(fs->oldvolfile, volfile, size); + fs->oldvollen = size; + ret = 0; + } - dict = dict_new (); - if (!dict) { - ret = -1; - goto out; - } + pthread_mutex_unlock(&fs->mutex); - // Set the supported min and max op-versions, so glusterd can make a - // decision - ret = dict_set_int32 (dict, "min-op-version", GD_OP_VERSION_MIN); - if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "Failed to set min-op-version" - " in request dict"); - goto out; - } + return ret; +} - ret = dict_set_int32 (dict, "max-op-version", GD_OP_VERSION_MAX); - if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "Failed to set max-op-version" - " in request dict"); - goto out; +int +glfs_mgmt_getspec_cbk(struct rpc_req *req, struct iovec *iov, int count, + void *myframe) +{ + gf_getspec_rsp rsp = { + 0, + }; + call_frame_t *frame = NULL; + glusterfs_ctx_t *ctx = NULL; + int ret = 0; + ssize_t size = 0; + FILE *tmpfp = NULL; + int need_retry = 0; + struct glfs *fs = NULL; + dict_t *dict = NULL; + char *servers_list = NULL; + int tmp_fd = -1; + char template[] = "/tmp/gfapi.volfile.XXXXXX"; + + frame = myframe; + ctx = frame->this->ctx; + + if (!ctx) { + gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_NULL, + "context", NULL); + errno = EINVAL; + ret = -1; + goto out; + } + + fs = ((xlator_t *)ctx->master)->private; + + if (-1 == req->rpc_status) { + ret = -1; + need_retry = 1; + goto out; + } + + ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_getspec_rsp); + if (ret < 0) { + gf_smsg(frame->this->name, GF_LOG_ERROR, 0, API_MSG_XDR_DECODE_FAILED, + NULL); + ret = -1; + goto out; + } + + if (-1 == rsp.op_ret) { + gf_smsg(frame->this->name, GF_LOG_ERROR, rsp.op_errno, + API_MSG_GET_VOLFILE_FAILED, "from server", NULL); + ret = -1; + errno = rsp.op_errno; + goto out; + } + + if (!rsp.xdata.xdata_len) { + goto volfile; + } + + dict = dict_new(); + if (!dict) { + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = dict_unserialize(rsp.xdata.xdata_val, rsp.xdata.xdata_len, &dict); + if (ret) { + gf_log(frame->this->name, GF_LOG_ERROR, + "failed to unserialize xdata to dictionary"); + goto out; + } + dict->extra_stdfree = rsp.xdata.xdata_val; + + /* glusterd2 only */ + ret = dict_get_str(dict, "servers-list", &servers_list); + if (ret) { + goto volfile; + } + + gf_log(frame->this->name, GF_LOG_INFO, + "Received list of available volfile servers: %s", servers_list); + + ret = gf_process_getspec_servers_list(&ctx->cmd_args, servers_list); + if (ret) { + gf_log(frame->this->name, GF_LOG_ERROR, + "Failed (%s) to process servers list: %s", strerror(errno), + servers_list); + } + +volfile: + ret = 0; + size = rsp.op_ret; + + pthread_mutex_lock(&fs->mutex); + if ((size == fs->oldvollen) && + (memcmp(fs->oldvolfile, rsp.spec, size) == 0)) { + pthread_mutex_unlock(&fs->mutex); + gf_smsg(frame->this->name, GF_LOG_INFO, 0, API_MSG_VOLFILE_INFO, NULL); + goto out; + } + pthread_mutex_unlock(&fs->mutex); + + /* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */ + tmp_fd = mkstemp(template); + if (-1 == tmp_fd) { + ret = -1; + goto out; + } + + /* Calling unlink so that when the file is closed or program + * terminates the temporary file is deleted. + */ + ret = sys_unlink(template); + if (ret < 0) { + gf_smsg(frame->this->name, GF_LOG_INFO, 0, API_MSG_UNABLE_TO_DEL, + "template=%s", template, NULL); + ret = 0; + } + + tmpfp = fdopen(tmp_fd, "w+b"); + if (!tmpfp) { + ret = -1; + goto out; + } + + fwrite(rsp.spec, size, 1, tmpfp); + fflush(tmpfp); + if (ferror(tmpfp)) { + ret = -1; + goto out; + } + + /* Check if only options have changed. No need to reload the + * volfile if topology hasn't changed. + * glusterfs_volfile_reconfigure returns 3 possible return states + * return 0 =======> reconfiguration of options has succeeded + * return 1 =======> the graph has to be reconstructed and all + * the xlators should be inited return -1(or -ve) =======> Some Internal + * Error occurred during the operation + */ + + pthread_mutex_lock(&fs->mutex); + ret = gf_volfile_reconfigure(fs->oldvollen, tmpfp, fs->ctx, fs->oldvolfile); + pthread_mutex_unlock(&fs->mutex); + + if (ret == 0) { + gf_msg_debug("glusterfsd-mgmt", 0, + "No need to re-load " + "volfile, reconfigure done"); + ret = glusterfs_oldvolfile_update(fs, rsp.spec, size); + goto out; + } + + if (ret < 0) { + gf_msg_debug("glusterfsd-mgmt", 0, "Reconfigure failed !!"); + goto out; + } + + ret = glfs_process_volfp(fs, tmpfp); + /* tmpfp closed */ + tmpfp = NULL; + tmp_fd = -1; + if (ret) + goto out; + + ret = glusterfs_oldvolfile_update(fs, rsp.spec, size); +out: + STACK_DESTROY(frame->root); + + if (rsp.spec) + free(rsp.spec); + + if (dict) + dict_unref(dict); + + // Stop if server is running at an unsupported op-version + if (ENOTSUP == ret) { + gf_smsg("mgmt", GF_LOG_ERROR, ENOTSUP, API_MSG_WRONG_OPVERSION, NULL); + errno = ENOTSUP; + glfs_init_done(fs, -1); + } + + if (ret && ctx && !ctx->active) { + /* Do it only for the first time */ + /* Failed to get the volume file, something wrong, + restart the process */ + gf_smsg("glfs-mgmt", GF_LOG_ERROR, EINVAL, API_MSG_GET_VOLFILE_FAILED, + "key=%s", ctx->cmd_args.volfile_id, NULL); + if (!need_retry) { + if (!errno) + errno = EINVAL; + glfs_init_done(fs, -1); } + } - ret = dict_allocate_and_serialize (dict, &req.xdata.xdata_val, - &req.xdata.xdata_len); - if (ret < 0) { - gf_log (THIS->name, GF_LOG_ERROR, - "Failed to serialize dictionary"); - goto out; - } + if (tmpfp) + fclose(tmpfp); + else if (tmp_fd != -1) + sys_close(tmp_fd); - ret = mgmt_submit_request (&req, frame, ctx, &clnt_handshake_prog, - GF_HNDSK_GETSPEC, mgmt_getspec_cbk, - (xdrproc_t)xdr_gf_getspec_req); -out: - return ret; + return 0; } +int +glfs_volfile_fetch(struct glfs *fs) +{ + cmd_args_t *cmd_args = NULL; + gf_getspec_req req = { + 0, + }; + int ret = -1; + call_frame_t *frame = NULL; + glusterfs_ctx_t *ctx = NULL; + dict_t *dict = NULL; + + ctx = fs->ctx; + cmd_args = &ctx->cmd_args; + + req.key = cmd_args->volfile_id; + req.flags = 0; + + dict = dict_new(); + if (!dict) { + goto out; + } + + // Set the supported min and max op-versions, so glusterd can make a + // decision + ret = dict_set_int32(dict, "min-op-version", GD_OP_VERSION_MIN); + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_DICT_SET_FAILED, + "min-op-version", NULL); + goto out; + } + + ret = dict_set_int32(dict, "max-op-version", GD_OP_VERSION_MAX); + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_DICT_SET_FAILED, + "max-op-version", NULL); + goto out; + } + + /* Ask for a list of volfile (glusterd2 only) servers */ + if (GF_CLIENT_PROCESS == ctx->process_mode) { + req.flags = req.flags | GF_GETSPEC_FLAG_SERVERS_LIST; + } + + ret = dict_allocate_and_serialize(dict, &req.xdata.xdata_val, + &req.xdata.xdata_len); + if (ret < 0) { + gf_smsg(THIS->name, GF_LOG_ERROR, 0, API_MSG_DICT_SERIALIZE_FAILED, + NULL); + goto out; + } + + frame = create_frame(THIS, ctx->pool); + if (!frame) { + ret = -1; + goto out; + } + + ret = mgmt_submit_request(&req, frame, ctx, &clnt_handshake_prog, + GF_HNDSK_GETSPEC, glfs_mgmt_getspec_cbk, + (xdrproc_t)xdr_gf_getspec_req); +out: + if (req.xdata.xdata_val) + GF_FREE(req.xdata.xdata_val); + if (dict) + dict_unref(dict); + + return ret; +} static int -mgmt_rpc_notify (struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, - void *data) +mgmt_rpc_notify(struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, + void *data) { - xlator_t *this = NULL; - cmd_args_t *cmd_args = NULL; - glusterfs_ctx_t *ctx = NULL; - struct glfs *fs = NULL; - int ret = 0; - - this = mydata; - ctx = this->ctx; - fs = ((xlator_t *)ctx->master)->private; - cmd_args = &ctx->cmd_args; - - switch (event) { - case RPC_CLNT_DISCONNECT: - if (!ctx->active) { - cmd_args->max_connect_attempts--; - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "failed to connect with remote-host: %s", - strerror (errno)); - gf_log ("glfs-mgmt", GF_LOG_INFO, - "%d connect attempts left", - cmd_args->max_connect_attempts); - if (0 >= cmd_args->max_connect_attempts) - glfs_init_done (fs, -1); - break; - } - break; - case RPC_CLNT_CONNECT: - rpc_clnt_set_connected (&((struct rpc_clnt*)ctx->mgmt)->conn); - - ret = glfs_volfile_fetch (fs); - if (ret && ctx && (ctx->active == NULL)) { - /* Do it only for the first time */ - /* Exit the process.. there is some wrong options */ - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "failed to fetch volume file (key:%s)", - ctx->cmd_args.volfile_id); - glfs_init_done (fs, -1); - } - - break; - default: - break; - } - - return 0; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + server_cmdline_t *server = NULL; + rpc_transport_t *rpc_trans = NULL; + struct glfs *fs = NULL; + int ret = 0; + struct dnscache6 *dnscache = NULL; + + this = mydata; + rpc_trans = rpc->conn.trans; + + ctx = this->ctx; + if (!ctx) + goto out; + + fs = ((xlator_t *)ctx->master)->private; + + switch (event) { + case RPC_CLNT_DISCONNECT: + if (!ctx->active) { + if (rpc_trans->connect_failed) + gf_smsg("glfs-mgmt", GF_LOG_ERROR, 0, + API_MSG_REMOTE_HOST_CONN_FAILED, "server=%s", + ctx->cmd_args.volfile_server, NULL); + else + gf_smsg("glfs-mgmt", GF_LOG_INFO, 0, + API_MSG_REMOTE_HOST_CONN_FAILED, "server=%s", + ctx->cmd_args.volfile_server, NULL); + + if (!rpc->disabled) { + /* + * Check if dnscache is exhausted for current server + * and continue until cache is exhausted + */ + dnscache = rpc_trans->dnscache; + if (dnscache && dnscache->next) { + break; + } + } + server = ctx->cmd_args.curr_server; + if (server->list.next == &ctx->cmd_args.volfile_servers) { + errno = ENOTCONN; + gf_smsg("glfs-mgmt", GF_LOG_INFO, ENOTCONN, + API_MSG_VOLFILE_SERVER_EXHAUST, NULL); + glfs_init_done(fs, -1); + break; + } + server = list_entry(server->list.next, typeof(*server), list); + ctx->cmd_args.curr_server = server; + ctx->cmd_args.volfile_server_port = server->port; + ctx->cmd_args.volfile_server = server->volfile_server; + ctx->cmd_args.volfile_server_transport = server->transport; + + ret = dict_set_str(rpc_trans->options, "transport-type", + server->transport); + if (ret != 0) { + gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, + API_MSG_DICT_SET_FAILED, "transport-type=%s", + server->transport, NULL); + errno = ENOTCONN; + glfs_init_done(fs, -1); + break; + } + + if (strcmp(server->transport, "unix") == 0) { + ret = dict_set_str(rpc_trans->options, + "transport.socket.connect-path", + server->volfile_server); + if (ret != 0) { + gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, + API_MSG_DICT_SET_FAILED, + "socket.connect-path=%s", + server->volfile_server, NULL); + errno = ENOTCONN; + glfs_init_done(fs, -1); + break; + } + /* delete the remote-host and remote-port keys + * in case they were set while looping through + * list of volfile servers previously + */ + dict_del(rpc_trans->options, "remote-host"); + dict_del(rpc_trans->options, "remote-port"); + } else { + ret = dict_set_int32(rpc_trans->options, "remote-port", + server->port); + if (ret != 0) { + gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, + API_MSG_DICT_SET_FAILED, "remote-port=%d", + server->port, NULL); + errno = ENOTCONN; + glfs_init_done(fs, -1); + break; + } + + ret = dict_set_str(rpc_trans->options, "remote-host", + server->volfile_server); + if (ret != 0) { + gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, + API_MSG_DICT_SET_FAILED, "remote-host=%s", + server->volfile_server, NULL); + errno = ENOTCONN; + glfs_init_done(fs, -1); + break; + } + /* delete the "transport.socket.connect-path" + * key in case if it was set while looping + * through list of volfile servers previously + */ + dict_del(rpc_trans->options, + "transport.socket.connect-path"); + } + + gf_smsg("glfs-mgmt", GF_LOG_INFO, 0, API_MSG_VOLFILE_CONNECTING, + "server=%s", server->volfile_server, "port=%d", + server->port, "transport=%s", server->transport, NULL); + } + break; + case RPC_CLNT_CONNECT: + ret = glfs_volfile_fetch(fs); + if (ret && (ctx->active == NULL)) { + /* Do it only for the first time */ + /* Exit the process.. there are some wrong options */ + gf_smsg("glfs-mgmt", GF_LOG_ERROR, EINVAL, + API_MSG_GET_VOLFILE_FAILED, "key=%s", + ctx->cmd_args.volfile_id, NULL); + errno = EINVAL; + glfs_init_done(fs, -1); + } + + break; + default: + break; + } +out: + return 0; } - int -glusterfs_mgmt_notify (int32_t op, void *data, ...) +glusterfs_mgmt_notify(int32_t op, void *data, ...) { - int ret = 0; + int ret = 0; - switch (op) - { - case GF_EN_DEFRAG_STATUS: - break; + switch (op) { + case GF_EN_DEFRAG_STATUS: + break; - default: - break; - } + default: + break; + } - return ret; + return ret; } - int -glfs_mgmt_init (struct glfs *fs) +glfs_mgmt_init(struct glfs *fs) { - cmd_args_t *cmd_args = NULL; - struct rpc_clnt *rpc = NULL; - dict_t *options = NULL; - int ret = -1; - int port = GF_DEFAULT_BASE_PORT; - char *host = NULL; - glusterfs_ctx_t *ctx = NULL; - - ctx = fs->ctx; - cmd_args = &ctx->cmd_args; - - if (ctx->mgmt) - return 0; - - if (cmd_args->volfile_server_port) - port = cmd_args->volfile_server_port; - - host = "localhost"; - if (cmd_args->volfile_server) - host = cmd_args->volfile_server; - - ret = rpc_transport_inet_options_build (&options, host, port); - if (ret) - goto out; - - rpc = rpc_clnt_new (options, THIS->ctx, THIS->name, 8); - if (!rpc) { - ret = -1; - gf_log (THIS->name, GF_LOG_WARNING, - "failed to create rpc clnt"); - goto out; - } - - ret = rpc_clnt_register_notify (rpc, mgmt_rpc_notify, THIS); - if (ret) { - gf_log (THIS->name, GF_LOG_WARNING, - "failed to register notify function"); - goto out; - } - - ret = rpcclnt_cbk_program_register (rpc, &mgmt_cbk_prog, THIS); - if (ret) { - gf_log (THIS->name, GF_LOG_WARNING, - "failed to register callback function"); - goto out; - } - - ctx->notify = glusterfs_mgmt_notify; - - /* This value should be set before doing the 'rpc_clnt_start()' as - the notify function uses this variable */ - ctx->mgmt = rpc; - - ret = rpc_clnt_start (rpc); + cmd_args_t *cmd_args = NULL; + struct rpc_clnt *rpc = NULL; + dict_t *options = NULL; + int ret = -1; + int port = GF_DEFAULT_BASE_PORT; + char *host = NULL; + glusterfs_ctx_t *ctx = NULL; + + ctx = fs->ctx; + cmd_args = &ctx->cmd_args; + + if (ctx->mgmt) + return 0; + + options = dict_new(); + if (!options) + goto out; + + if (cmd_args->volfile_server_port) + port = cmd_args->volfile_server_port; + + if (cmd_args->volfile_server) { + host = cmd_args->volfile_server; + } else if (cmd_args->volfile_server_transport && + !strcmp(cmd_args->volfile_server_transport, "unix")) { + host = DEFAULT_GLUSTERD_SOCKFILE; + } else { + host = "localhost"; + } + + if (cmd_args->volfile_server_transport && + !strcmp(cmd_args->volfile_server_transport, "unix")) { + ret = rpc_transport_unix_options_build(options, host, 0); + } else { + xlator_cmdline_option_t *opt = find_xlator_option_in_cmd_args_t( + "address-family", cmd_args); + ret = rpc_transport_inet_options_build(options, host, port, + (opt ? opt->value : NULL)); + } + + if (ret) + goto out; + + rpc = rpc_clnt_new(options, THIS, THIS->name, 8); + if (!rpc) { + ret = -1; + gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_CREATE_RPC_CLIENT_FAILED, + NULL); + goto out; + } + + ret = rpc_clnt_register_notify(rpc, mgmt_rpc_notify, THIS); + if (ret) { + gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_REG_NOTIFY_FUNC_FAILED, + NULL); + goto out; + } + + ret = rpcclnt_cbk_program_register(rpc, &mgmt_cbk_prog, THIS); + if (ret) { + gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_REG_CBK_FUNC_FAILED, + NULL); + goto out; + } + + ctx->notify = glusterfs_mgmt_notify; + + /* This value should be set before doing the 'rpc_clnt_start()' as + the notify function uses this variable */ + ctx->mgmt = rpc; + + ret = rpc_clnt_start(rpc); out: - return ret; + if (options) + dict_unref(options); + return ret; } - diff --git a/api/src/glfs-resolve.c b/api/src/glfs-resolve.c index 4a634df6ec1..8a393ecb464 100644 --- a/api/src/glfs-resolve.c +++ b/api/src/glfs-resolve.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,7 +8,6 @@ cases as published by the Free Software Foundation. */ - #include <unistd.h> #include <string.h> #include <stdlib.h> @@ -16,864 +15,1185 @@ #include <inttypes.h> #include <limits.h> -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif - -#include "glusterfs.h" -#include "logging.h" -#include "stack.h" -#include "event.h" +#include <glusterfs/glusterfs.h> +#include <glusterfs/logging.h> +#include <glusterfs/stack.h> +#include <glusterfs/gf-event.h> #include "glfs-mem-types.h" -#include "common-utils.h" -#include "syncop.h" -#include "call-stub.h" - +#include <glusterfs/common-utils.h> +#include <glusterfs/syncop.h> +#include <glusterfs/call-stub.h> +#include "gfapi-messages.h" +#include <glusterfs/inode.h> #include "glfs-internal.h" -#define graphid_str(subvol) (uuid_utoa((unsigned char *)subvol->graph->graph_uuid)) - - +#define graphid_str(subvol) \ + (uuid_utoa((unsigned char *)subvol->graph->graph_uuid)) int -glfs_first_lookup_safe (xlator_t *subvol) +glfs_first_lookup_safe(xlator_t *subvol) { - loc_t loc = {0, }; - int ret = -1; + loc_t loc = { + 0, + }; + int ret = -1; - loc.inode = subvol->itable->root; - memset (loc.gfid, 0, 16); - loc.gfid[15] = 1; - loc.path = "/"; - loc.name = ""; + loc.inode = subvol->itable->root; + memset(loc.gfid, 0, 16); + loc.gfid[15] = 1; + loc.path = "/"; + loc.name = ""; - ret = syncop_lookup (subvol, &loc, 0, 0, 0, 0); + ret = syncop_lookup(subvol, &loc, 0, 0, 0, 0); + DECODE_SYNCOP_ERR(ret); - gf_log (subvol->name, GF_LOG_DEBUG, "first lookup complete %d", ret); + gf_msg_debug(subvol->name, 0, "first lookup complete %d", ret); - return ret; + return ret; } - int -__glfs_first_lookup (struct glfs *fs, xlator_t *subvol) +__glfs_first_lookup(struct glfs *fs, xlator_t *subvol) { - int ret = -1; + int ret = -1; - fs->migration_in_progress = 1; - pthread_mutex_unlock (&fs->mutex); - { - ret = glfs_first_lookup_safe (subvol); - } - pthread_mutex_lock (&fs->mutex); - fs->migration_in_progress = 0; + fs->migration_in_progress = 1; + pthread_mutex_unlock(&fs->mutex); + { + ret = glfs_first_lookup_safe(subvol); + } + pthread_mutex_lock(&fs->mutex); + fs->migration_in_progress = 0; + pthread_cond_broadcast(&fs->cond); - return ret; -} + /* wake up other waiting tasks */ + __GLFS_SYNCTASK_WAKE(fs); + return ret; +} +/** + * We have to check if need_lookup flag is set in both old and the new inodes. + * If its set in oldinode, then directly go ahead and do an explicit lookup. + * But if its not set in the oldinode, then check if the newinode is linked + * via readdirp. If so an explicit lookup is needed on the new inode, so that + * below xlators can set their respective contexts. + */ inode_t * -glfs_refresh_inode_safe (xlator_t *subvol, inode_t *oldinode) +glfs_refresh_inode_safe(xlator_t *subvol, inode_t *oldinode, + gf_boolean_t need_lookup) { - loc_t loc = {0, }; - int ret = -1; - struct iatt iatt = {0, }; - inode_t *newinode = NULL; - - - if (!oldinode) - return NULL; - - if (oldinode->table->xl == subvol) - return inode_ref (oldinode); - - newinode = inode_find (subvol->itable, oldinode->gfid); - if (newinode) - return newinode; - - uuid_copy (loc.gfid, oldinode->gfid); - loc.inode = inode_new (subvol->itable); - if (!loc.inode) - return NULL; - - ret = syncop_lookup (subvol, &loc, 0, &iatt, 0, 0); + loc_t loc = { + 0, + }; + int ret = -1; + struct iatt iatt = { + 0, + }; + inode_t *newinode = NULL; + gf_boolean_t lookup_needed = _gf_false; + uint64_t ctx_value = LOOKUP_NOT_NEEDED; + + if (!oldinode) + return NULL; + + if (!need_lookup && oldinode->table->xl == subvol) + return inode_ref(oldinode); + + newinode = inode_find(subvol->itable, oldinode->gfid); + if (!need_lookup && newinode) { + lookup_needed = inode_needs_lookup(newinode, THIS); + if (!lookup_needed) + return newinode; + } + + gf_uuid_copy(loc.gfid, oldinode->gfid); + if (!newinode) + loc.inode = inode_new(subvol->itable); + else + loc.inode = newinode; + + if (!loc.inode) + return NULL; + + ret = syncop_lookup(subvol, &loc, &iatt, 0, 0, 0); + DECODE_SYNCOP_ERR(ret); + + if (ret) { + gf_smsg(subvol->name, GF_LOG_WARNING, errno, + API_MSG_INODE_REFRESH_FAILED, "gfid=%s", + uuid_utoa(oldinode->gfid), "err=%s", strerror(errno), NULL); + loc_wipe(&loc); + return NULL; + } + + newinode = inode_link(loc.inode, 0, 0, &iatt); + if (newinode) { + if (newinode == loc.inode) + inode_ctx_set(newinode, THIS, &ctx_value); + inode_lookup(newinode); + } else { + gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, + "gfid=%s", uuid_utoa((unsigned char *)&iatt.ia_gfid), NULL); + } + + loc_wipe(&loc); + + return newinode; +} - if (ret) { - gf_log (subvol->name, GF_LOG_WARNING, - "inode refresh of %s failed: %s", - uuid_utoa (oldinode->gfid), strerror (errno)); - loc_wipe (&loc); - return NULL; - } +inode_t * +__glfs_refresh_inode(struct glfs *fs, xlator_t *subvol, inode_t *inode, + gf_boolean_t need_lookup) +{ + inode_t *newinode = NULL; - newinode = inode_link (loc.inode, 0, 0, &iatt); - if (newinode) - inode_lookup (newinode); + fs->migration_in_progress = 1; + pthread_mutex_unlock(&fs->mutex); + { + newinode = glfs_refresh_inode_safe(subvol, inode, need_lookup); + } + pthread_mutex_lock(&fs->mutex); + fs->migration_in_progress = 0; + pthread_cond_broadcast(&fs->cond); - loc_wipe (&loc); + /* wake up other waiting tasks */ + __GLFS_SYNCTASK_WAKE(fs); - return newinode; + return newinode; } - -inode_t * -__glfs_refresh_inode (struct glfs *fs, xlator_t *subvol, inode_t *inode) +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_loc_touchup, 3.4.0) +int +priv_glfs_loc_touchup(loc_t *loc) { - inode_t *newinode = NULL; + int ret = 0; - fs->migration_in_progress = 1; - pthread_mutex_unlock (&fs->mutex); - { - newinode = glfs_refresh_inode_safe (subvol, inode); - } - pthread_mutex_lock (&fs->mutex); - fs->migration_in_progress = 0; + ret = loc_touchup(loc, loc->name); + if (ret < 0) { + errno = -ret; + ret = -1; + } - return newinode; + return ret; } int -glfs_loc_touchup (loc_t *loc) +glfs_resolve_symlink(struct glfs *fs, xlator_t *subvol, inode_t *inode, + char **lpath) { - char *path = NULL; - int ret = -1; - char *bn = NULL; - - if (loc->parent) - ret = inode_path (loc->parent, loc->name, &path); - else - ret = inode_path (loc->inode, 0, &path); - - loc->path = path; - - if (ret < 0 || !path) { - ret = -1; - errno = ENOMEM; - goto out; - } - - bn = strrchr (path, '/'); - if (bn) - bn++; - loc->name = bn; - ret = 0; + loc_t loc = { + 0, + }; + char *path = NULL; + char *rpath = NULL; + int ret = -1; + + loc.inode = inode_ref(inode); + gf_uuid_copy(loc.gfid, inode->gfid); + ret = inode_path(inode, NULL, &rpath); + if (ret < 0) + goto out; + loc.path = rpath; + + ret = syncop_readlink(subvol, &loc, &path, 4096, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + + if (ret < 0) + goto out; + + if (lpath) + *lpath = path; out: - return ret; + loc_wipe(&loc); + return ret; } - int -glfs_resolve_symlink (struct glfs *fs, xlator_t *subvol, inode_t *inode, - char **lpath) +glfs_resolve_base(struct glfs *fs, xlator_t *subvol, inode_t *inode, + struct iatt *iatt) { - loc_t loc = {0, }; - char *path = NULL; - char *rpath = NULL; - int ret = -1; - - loc.inode = inode_ref (inode); - uuid_copy (loc.gfid, inode->gfid); - ret = inode_path (inode, NULL, &rpath); - if (ret < 0) - goto out; - loc.path = rpath; - - ret = syncop_readlink (subvol, &loc, &path, 4096); - - if (ret < 0) - goto out; - - if (lpath) - *lpath = path; + loc_t loc = { + 0, + }; + int ret = -1; + char *path = NULL; + + loc.inode = inode_ref(inode); + gf_uuid_copy(loc.gfid, inode->gfid); + + ret = inode_path(loc.inode, NULL, &path); + loc.path = path; + if (ret < 0) + goto out; + + ret = syncop_lookup(subvol, &loc, iatt, NULL, NULL, NULL); + DECODE_SYNCOP_ERR(ret); out: - loc_wipe (&loc); - return ret; -} + loc_wipe(&loc); - -void -glfs_resolve_base (struct glfs *fs, xlator_t *subvol, inode_t *inode, - struct iatt *iatt) + return ret; +} +/* + * This function can be used to call named lookup on root. + * If you use glfs_resolve_base, that will be a nameless lookup. + */ +static int +glfs_resolve_root(struct glfs *fs, xlator_t *subvol, inode_t *inode, + struct iatt *iatt) { - loc_t loc = {0, }; - int ret = -1; - char *path = NULL; - - loc.inode = inode_ref (inode); - uuid_copy (loc.gfid, inode->gfid); - - ret = inode_path (loc.inode, NULL, &path); - loc.path = path; - if (ret < 0) - goto out; - - ret = syncop_lookup (subvol, &loc, NULL, iatt, NULL, NULL); + loc_t loc = { + 0, + }; + int ret = -1; + char *path = NULL; + + loc.inode = inode_ref(inode); + + ret = inode_path(loc.inode, ".", &path); + loc.path = path; + loc.name = "."; + /* Having a value in loc.name will help to bypass md-cache check for + * nameless lookup. + * TODO: Re-visit on nameless lookup and md-cache. + * Github issue : https://github.com/gluster/glusterfs/issues/232 + */ + loc.parent = inode_ref(inode); + if (ret < 0) + goto out; + + ret = syncop_lookup(subvol, &loc, iatt, NULL, NULL, NULL); + DECODE_SYNCOP_ERR(ret); out: - loc_wipe (&loc); -} + loc_wipe(&loc); + return ret; +} inode_t * -glfs_resolve_component (struct glfs *fs, xlator_t *subvol, inode_t *parent, - const char *component, struct iatt *iatt, - int force_lookup) +glfs_resolve_component(struct glfs *fs, xlator_t *subvol, inode_t *parent, + const char *component, struct iatt *iatt, + int force_lookup) { - loc_t loc = {0, }; - inode_t *inode = NULL; - int reval = 0; - int ret = -1; - int glret = -1; - struct iatt ciatt = {0, }; - uuid_t gfid; - dict_t *xattr_req = NULL; - - loc.name = component; - - loc.parent = inode_ref (parent); - uuid_copy (loc.pargfid, parent->gfid); - - - if (strcmp (component, ".") == 0) - loc.inode = inode_ref (parent); - else if (strcmp (component, "..") == 0) - loc.inode = inode_parent (parent, 0, 0); - else - loc.inode = inode_grep (parent->table, parent, component); - - if (loc.inode) { - uuid_copy (loc.gfid, loc.inode->gfid); - reval = 1; - - if (!force_lookup) { - inode = inode_ref (loc.inode); - ciatt.ia_type = inode->ia_type; - goto found; - } - } else { - uuid_generate (gfid); - loc.inode = inode_new (parent->table); - } - - if (!loc.inode) - goto out; - - glret = glfs_loc_touchup (&loc); - if (glret < 0) { - ret = -1; - goto out; - } - - ret = syncop_lookup (subvol, &loc, NULL, &ciatt, NULL, NULL); - if (ret && reval) { - inode_unref (loc.inode); - loc.inode = inode_new (parent->table); - if (!loc.inode) { - errno = ENOMEM; - goto out; - } - - xattr_req = dict_new (); - if (!xattr_req) { - errno = ENOMEM; - goto out; - } - - uuid_generate (gfid); - - ret = dict_set_static_bin (xattr_req, "gfid-req", gfid, 16); - if (ret) { - errno = ENOMEM; - goto out; - } - - ret = syncop_lookup (subvol, &loc, xattr_req, &ciatt, - NULL, NULL); - } - if (ret) - goto out; - - inode = inode_link (loc.inode, loc.parent, component, &ciatt); + loc_t loc = { + 0, + }; + inode_t *inode = NULL; + inode_t *temp_parent = NULL; + int reval = 0; + int ret = -1; + int glret = -1; + struct iatt ciatt = { + 0, + }; + uuid_t gfid; + dict_t *xattr_req = NULL; + uint64_t ctx_value = LOOKUP_NOT_NEEDED; + + loc.parent = inode_ref(parent); + gf_uuid_copy(loc.pargfid, parent->gfid); + + if (__is_root_gfid(parent->gfid) && + ((strcmp(component, ".") == 0) || (strcmp(component, "..") == 0) || + (strcmp(component, "") == 0))) { + if (!force_lookup) { + inode = inode_ref(parent); + } else { + ret = glfs_resolve_root(fs, subvol, parent, &ciatt); + if (!ret) + inode = inode_ref(parent); + } + goto found; + } + /* * + * if the component name is either "." or "..", it will try to + * resolve that if inode has a proper parent (named lookup). + * + * Below condition works like this + * + * Example 1 : + * Path /out_dir/dir/in_dir/. + * In put values : + * parent = in_dir + * component : "." + * + * Out put values: + * parent : dir + * component : "in_dir" + * + * Example 2 : + * Path /out_dir/dir/in_dir/.. + * In put values : + * parent = in_dir + * component : ".." + * + * Out put values: + * parent : output_dir + * component : "dir" + * + * In case of nameless lookup, both "." and ".." retained + */ + + if (strcmp(component, ".") == 0) { + loc.inode = inode_ref(parent); + temp_parent = inode_parent(loc.inode, 0, 0); + if (temp_parent) { + inode_unref(loc.parent); + loc.parent = temp_parent; + gf_uuid_copy(loc.pargfid, temp_parent->gfid); + inode_find_directory_name(loc.inode, &loc.name); + } + + } else if (strcmp(component, "..") == 0) { + loc.inode = inode_parent(parent, 0, 0); + if (loc.inode) { + temp_parent = inode_parent(loc.inode, 0, 0); + if (temp_parent) { + inode_unref(loc.parent); + loc.parent = temp_parent; + gf_uuid_copy(loc.pargfid, temp_parent->gfid); + inode_find_directory_name(loc.inode, &loc.name); + } else if (__is_root_gfid(loc.inode->gfid)) { + inode_unref(loc.parent); + loc.parent = inode_ref(loc.inode); + gf_uuid_copy(loc.pargfid, loc.inode->gfid); + loc.name = "."; + } else { + inode_unref(loc.inode); + loc.inode = NULL; + } + } + } else + loc.inode = inode_grep(parent->table, parent, component); + + if (!loc.name) + loc.name = component; + + if (loc.inode) { + gf_uuid_copy(loc.gfid, loc.inode->gfid); + reval = 1; + + if (!(force_lookup || inode_needs_lookup(loc.inode, THIS))) { + inode = inode_ref(loc.inode); + goto found; + } + } else { + gf_uuid_generate(gfid); + loc.inode = inode_new(parent->table); + if (!loc.inode) { + errno = ENOMEM; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + errno = ENOMEM; + goto out; + } + + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + errno = ENOMEM; + goto out; + } + } + + glret = priv_glfs_loc_touchup(&loc); + if (glret < 0) { + ret = -1; + goto out; + } + + ret = syncop_lookup(subvol, &loc, &ciatt, NULL, xattr_req, NULL); + if (ret && reval) { + /* + * A stale mapping might exist for a dentry/inode that has been + * removed from another client. + */ + if (-ret == ENOENT) { + inode_unlink(loc.inode, loc.parent, loc.name); + if (!inode_has_dentry(loc.inode)) + inode_forget(loc.inode, 0); + } + + inode_unref(loc.inode); + gf_uuid_clear(loc.gfid); + loc.inode = inode_new(parent->table); + if (!loc.inode) { + errno = ENOMEM; + goto out; + } + + xattr_req = dict_new(); + if (!xattr_req) { + errno = ENOMEM; + goto out; + } + + gf_uuid_generate(gfid); + + ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); + if (ret) { + errno = ENOMEM; + goto out; + } + + ret = syncop_lookup(subvol, &loc, &ciatt, NULL, xattr_req, NULL); + } + DECODE_SYNCOP_ERR(ret); + if (ret) + goto out; + + inode = inode_link(loc.inode, loc.parent, component, &ciatt); + + if (!inode) { + gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, + "gfid=%s", uuid_utoa((unsigned char *)&ciatt.ia_gfid), NULL); + goto out; + } else if (inode == loc.inode) + inode_ctx_set(inode, THIS, &ctx_value); found: - if (inode) - inode_lookup (inode); - if (iatt) - *iatt = ciatt; + if (inode) { + ciatt.ia_type = inode->ia_type; + inode_lookup(inode); + } + if (iatt) + *iatt = ciatt; out: - if (xattr_req) - dict_unref (xattr_req); + if (xattr_req) + dict_unref(xattr_req); + loc_wipe(&loc); - loc_wipe (&loc); - - return inode; + return inode; } - +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_resolve_at, 3.4.0) int -glfs_resolve_at (struct glfs *fs, xlator_t *subvol, inode_t *at, - const char *origpath, loc_t *loc, struct iatt *iatt, - int follow, int reval) +priv_glfs_resolve_at(struct glfs *fs, xlator_t *subvol, inode_t *at, + const char *origpath, loc_t *loc, struct iatt *iatt, + int follow, int reval) { - inode_t *inode = NULL; - inode_t *parent = NULL; - char *saveptr = NULL; - char *path = NULL; - char *component = NULL; - char *next_component = NULL; - int ret = -1; - struct iatt ciatt = {0, }; - - path = gf_strdup (origpath); - if (!path) { - errno = ENOMEM; - return -1; - } - - parent = NULL; - if (at && path[0] != '/') { - /* A relative resolution of a path which starts with '/' - is equal to an absolute path resolution. - */ - inode = inode_ref (at); - } else { - inode = inode_ref (subvol->itable->root); - - glfs_resolve_base (fs, subvol, inode, &ciatt); - } - - for (component = strtok_r (path, "/", &saveptr); - component; component = next_component) { - - next_component = strtok_r (NULL, "/", &saveptr); - - if (parent) - inode_unref (parent); - - parent = inode; - - inode = glfs_resolve_component (fs, subvol, parent, - component, &ciatt, - /* force hard lookup on the last - component, as the caller - wants proper iatt filled - */ - (reval || !next_component)); - if (!inode) - break; - - if (IA_ISLNK (ciatt.ia_type) && (next_component || follow)) { - /* If the component is not the last piece, - then following it is necessary even if - not requested by the caller - */ - char *lpath = NULL; - loc_t sym_loc = {0,}; - - ret = glfs_resolve_symlink (fs, subvol, inode, &lpath); - inode_unref (inode); - inode = NULL; - if (ret < 0) - break; - - ret = glfs_resolve_at (fs, subvol, parent, lpath, - &sym_loc, - /* followed iatt becomes the - component iatt - */ - &ciatt, - /* always recurisvely follow while - following symlink - */ - 1, reval); - if (ret == 0) - inode = inode_ref (sym_loc.inode); - loc_wipe (&sym_loc); - GF_FREE (lpath); - } - - if (!next_component) - break; - - if (!IA_ISDIR (ciatt.ia_type)) { - /* next_component exists and this component is - not a directory - */ - inode_unref (inode); - inode = NULL; - ret = -1; - errno = ENOTDIR; - break; - } - } - - if (parent && next_component) - /* resolution failed mid-way */ - goto out; - - /* At this point, all components up to the last parent directory - have been resolved successfully (@parent). Resolution of basename - might have failed (@inode) if at all. - */ - - loc->parent = parent; - if (parent) { - uuid_copy (loc->pargfid, parent->gfid); - loc->name = component; - } - - loc->inode = inode; - if (inode) { - uuid_copy (loc->gfid, inode->gfid); - if (iatt) - *iatt = ciatt; - ret = 0; - } - - glfs_loc_touchup (loc); + inode_t *inode = NULL; + inode_t *parent = NULL; + char *saveptr = NULL; + char *path = NULL; + char *component = NULL; + char *next_component = NULL; + int ret = -1; + struct iatt ciatt = { + 0, + }; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (origpath[0] == '\0') { + errno = EINVAL; + goto invalid_fs; + } + + parent = NULL; + if (at && origpath[0] != '/') { + /* A relative resolution of a path which starts with '/' + is equal to an absolute path resolution. + */ + inode = inode_ref(at); + } else { + inode = inode_ref(subvol->itable->root); + + if (strcmp(origpath, "/") == 0) + glfs_resolve_root(fs, subvol, inode, &ciatt); + } + + path = gf_strdup(origpath); + if (!path) + goto invalid_fs; + + for (component = strtok_r(path, "/", &saveptr); component; + component = next_component) { + next_component = strtok_r(NULL, "/", &saveptr); + + if (parent) + inode_unref(parent); + parent = inode; + inode = glfs_resolve_component(fs, subvol, parent, component, &ciatt, + /* force hard lookup on the last + component, as the caller + wants proper iatt filled + */ + (reval || (!next_component && iatt))); + if (!inode) { + ret = -1; + break; + } + + if (IA_ISLNK(ciatt.ia_type) && (next_component || follow)) { + /* If the component is not the last piece, + then following it is necessary even if + not requested by the caller + */ + char *lpath = NULL; + loc_t sym_loc = { + 0, + }; + + if (follow > GLFS_SYMLINK_MAX_FOLLOW) { + errno = ELOOP; + ret = -1; + if (inode) { + inode_unref(inode); + inode = NULL; + } + break; + } + + ret = glfs_resolve_symlink(fs, subvol, inode, &lpath); + inode_unref(inode); + inode = NULL; + if (ret < 0) + break; + + ret = priv_glfs_resolve_at(fs, subvol, parent, lpath, &sym_loc, + /* followed iatt becomes the + component iatt + */ + &ciatt, + /* always recurisvely follow while + following symlink + */ + follow + 1, reval); + if (ret == 0) + inode = inode_ref(sym_loc.inode); + loc_wipe(&sym_loc); + GF_FREE(lpath); + } + + if (!next_component) + break; + + if (!IA_ISDIR(ciatt.ia_type)) { + /* next_component exists and this component is + not a directory + */ + inode_unref(inode); + inode = NULL; + ret = -1; + errno = ENOTDIR; + break; + } + } + + if (parent && next_component) + /* resolution failed mid-way */ + goto out; + + /* At this point, all components up to the last parent directory + have been resolved successfully (@parent). Resolution of basename + might have failed (@inode) if at all. + */ + + loc->parent = parent; + if (parent) { + gf_uuid_copy(loc->pargfid, parent->gfid); + loc->name = component; + } + + loc->inode = inode; + if (inode) { + gf_uuid_copy(loc->gfid, inode->gfid); + if (iatt) + *iatt = ciatt; + ret = 0; + } + + if (priv_glfs_loc_touchup(loc) < 0) { + ret = -1; + } out: - GF_FREE (path); - - /* do NOT loc_wipe here as only last component might be missing */ + GF_FREE(path); + __GLFS_EXIT_FS; - return ret; + /* do NOT loc_wipe here as only last component might be missing */ +invalid_fs: + return ret; } - int -glfs_resolve_path (struct glfs *fs, xlator_t *subvol, const char *origpath, - loc_t *loc, struct iatt *iatt, int follow, int reval) +glfs_resolve_path(struct glfs *fs, xlator_t *subvol, const char *origpath, + loc_t *loc, struct iatt *iatt, int follow, int reval) { - int ret = -1; - inode_t *cwd = NULL; - - if (origpath[0] == '/') - return glfs_resolve_at (fs, subvol, NULL, origpath, loc, iatt, - follow, reval); - - cwd = glfs_cwd_get (fs); + int ret = -1; + inode_t *cwd = NULL; + + if (origpath[0] == '/') + return priv_glfs_resolve_at(fs, subvol, NULL, origpath, loc, iatt, + follow, reval); + + cwd = glfs_cwd_get(fs); + if (NULL == cwd) { + gf_smsg(subvol->name, GF_LOG_WARNING, EIO, API_MSG_GET_CWD_FAILED, + NULL); + errno = EIO; + goto out; + } + + ret = priv_glfs_resolve_at(fs, subvol, cwd, origpath, loc, iatt, follow, + reval); + if (cwd) + inode_unref(cwd); - ret = glfs_resolve_at (fs, subvol, cwd, origpath, loc, iatt, - follow, reval); - if (cwd) - inode_unref (cwd); - - return ret; +out: + return ret; } - +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_resolve, 3.7.0) int -glfs_resolve (struct glfs *fs, xlator_t *subvol, const char *origpath, - loc_t *loc, struct iatt *iatt, int reval) +priv_glfs_resolve(struct glfs *fs, xlator_t *subvol, const char *origpath, + loc_t *loc, struct iatt *iatt, int reval) { - int ret = -1; + int ret = -1; - ret = glfs_resolve_path (fs, subvol, origpath, loc, iatt, 1, reval); + ret = glfs_resolve_path(fs, subvol, origpath, loc, iatt, 1, reval); - return ret; + return ret; } - int -glfs_lresolve (struct glfs *fs, xlator_t *subvol, const char *origpath, - loc_t *loc, struct iatt *iatt, int reval) +glfs_lresolve(struct glfs *fs, xlator_t *subvol, const char *origpath, + loc_t *loc, struct iatt *iatt, int reval) { - int ret = -1; + int ret = -1; - ret = glfs_resolve_path (fs, subvol, origpath, loc, iatt, 0, reval); + ret = glfs_resolve_path(fs, subvol, origpath, loc, iatt, 0, reval); - return ret; + return ret; } - int -glfs_migrate_fd_locks_safe (struct glfs *fs, xlator_t *oldsubvol, fd_t *oldfd, - xlator_t *newsubvol, fd_t *newfd) +glfs_migrate_fd_locks_safe(struct glfs *fs, xlator_t *oldsubvol, fd_t *oldfd, + xlator_t *newsubvol, fd_t *newfd) { - dict_t *lockinfo = NULL; - int ret = 0; - char uuid1[64]; - - if (!oldfd->lk_ctx || fd_lk_ctx_empty (oldfd->lk_ctx)) - return 0; - - newfd->lk_ctx = fd_lk_ctx_ref (oldfd->lk_ctx); - - ret = syncop_fgetxattr (oldsubvol, oldfd, &lockinfo, - GF_XATTR_LOCKINFO_KEY); - if (ret < 0) { - gf_log (fs->volname, GF_LOG_WARNING, - "fgetxattr (%s) failed (%s) on graph %s (%d)", - uuid_utoa_r (oldfd->inode->gfid, uuid1), - strerror (errno), - graphid_str (oldsubvol), oldsubvol->graph->id); - goto out; - } - - if (!dict_get (lockinfo, GF_XATTR_LOCKINFO_KEY)) { - gf_log (fs->volname, GF_LOG_WARNING, - "missing lokinfo key (%s) on graph %s (%d)", - uuid_utoa_r (oldfd->inode->gfid, uuid1), - graphid_str (oldsubvol), oldsubvol->graph->id); - goto out; - } - - ret = syncop_fsetxattr (newsubvol, newfd, lockinfo, 0); - if (ret < 0) { - gf_log (fs->volname, GF_LOG_WARNING, - "fsetxattr (%s) failed (%s) on graph %s (%d)", - uuid_utoa_r (newfd->inode->gfid, uuid1), - strerror (errno), - graphid_str (newsubvol), newsubvol->graph->id); - goto out; - } + dict_t *lockinfo = NULL; + int ret = 0; + char uuid1[64]; + + if (!oldfd->lk_ctx || fd_lk_ctx_empty(oldfd->lk_ctx)) + return 0; + + newfd->lk_ctx = fd_lk_ctx_ref(oldfd->lk_ctx); + + ret = syncop_fgetxattr(oldsubvol, oldfd, &lockinfo, GF_XATTR_LOCKINFO_KEY, + NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret < 0) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FGETXATTR_FAILED, + "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "err=%s", + strerror(errno), "subvol=%s", graphid_str(oldsubvol), "id=%d", + oldsubvol->graph->id, NULL); + goto out; + } + + if (!dict_get(lockinfo, GF_XATTR_LOCKINFO_KEY)) { + gf_smsg(fs->volname, GF_LOG_WARNING, 0, API_MSG_LOCKINFO_KEY_MISSING, + "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", + graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, NULL); + goto out; + } + + ret = syncop_fsetxattr(newsubvol, newfd, lockinfo, 0, NULL, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret < 0) { + gf_smsg(fs->volname, GF_LOG_WARNING, 0, API_MSG_FSETXATTR_FAILED, + "gfid=%s", uuid_utoa_r(newfd->inode->gfid, uuid1), "err=%s", + strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", + newsubvol->graph->id, NULL); + goto out; + } out: - if (lockinfo) - dict_unref (lockinfo); - return ret; + if (lockinfo) + dict_unref(lockinfo); + return ret; } - fd_t * -glfs_migrate_fd_safe (struct glfs *fs, xlator_t *newsubvol, fd_t *oldfd) +glfs_migrate_fd_safe(struct glfs *fs, xlator_t *newsubvol, fd_t *oldfd) { - fd_t *newfd = NULL; - inode_t *oldinode = NULL; - inode_t *newinode = NULL; - xlator_t *oldsubvol = NULL; - int ret = -1; - loc_t loc = {0, }; - char uuid1[64]; - - - oldinode = oldfd->inode; - oldsubvol = oldinode->table->xl; - - if (oldsubvol == newsubvol) - return fd_ref (oldfd); - - if (!oldsubvol->switched) { - ret = syncop_fsync (oldsubvol, oldfd, 0); - if (ret) { - gf_log (fs->volname, GF_LOG_WARNING, - "fsync() failed (%s) on %s graph %s (%d)", - strerror (errno), - uuid_utoa_r (oldfd->inode->gfid, uuid1), - graphid_str (oldsubvol), oldsubvol->graph->id); - } - } - - newinode = glfs_refresh_inode_safe (newsubvol, oldinode); - if (!newinode) { - gf_log (fs->volname, GF_LOG_WARNING, - "inode (%s) refresh failed (%s) on graph %s (%d)", - uuid_utoa_r (oldinode->gfid, uuid1), - strerror (errno), - graphid_str (newsubvol), newsubvol->graph->id); - goto out; - } - - newfd = fd_create (newinode, getpid()); - if (!newfd) { - gf_log (fs->volname, GF_LOG_WARNING, - "fd_create (%s) failed (%s) on graph %s (%d)", - uuid_utoa_r (newinode->gfid, uuid1), - strerror (errno), - graphid_str (newsubvol), newsubvol->graph->id); - goto out; - } - - loc.inode = inode_ref (newinode); - - if (IA_ISDIR (oldinode->ia_type)) - ret = syncop_opendir (newsubvol, &loc, newfd); - else - ret = syncop_open (newsubvol, &loc, - oldfd->flags & ~(O_TRUNC|O_EXCL|O_CREAT), - newfd); - loc_wipe (&loc); - - if (ret) { - gf_log (fs->volname, GF_LOG_WARNING, - "syncop_open%s (%s) failed (%s) on graph %s (%d)", - IA_ISDIR (oldinode->ia_type) ? "dir" : "", - uuid_utoa_r (newinode->gfid, uuid1), - strerror (errno), - graphid_str (newsubvol), newsubvol->graph->id); - goto out; - } - - ret = glfs_migrate_fd_locks_safe (fs, oldsubvol, oldfd, newsubvol, - newfd); - - if (ret) { - gf_log (fs->volname, GF_LOG_WARNING, - "lock migration (%s) failed (%s) on graph %s (%d)", - uuid_utoa_r (newinode->gfid, uuid1), - strerror (errno), - graphid_str (newsubvol), newsubvol->graph->id); - goto out; - } - - fd_bind (newfd); + fd_t *newfd = NULL; + inode_t *oldinode = NULL; + inode_t *newinode = NULL; + xlator_t *oldsubvol = NULL; + int ret = -1; + loc_t loc = { + 0, + }; + char uuid1[64]; + dict_t *xdata = NULL; + + oldinode = oldfd->inode; + oldsubvol = oldinode->table->xl; + + if (oldsubvol == newsubvol) + return fd_ref(oldfd); + + if (!oldsubvol->switched) { + xdata = dict_new(); + if (!xdata || dict_set_int8(xdata, "last-fsync", 1)) { + gf_smsg(fs->volname, GF_LOG_WARNING, ENOMEM, API_MSG_FSYNC_FAILED, + "err=%s", "last-fsync set failed", "gfid=%s", + uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", + graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, + NULL); + } + + ret = syncop_fsync(oldsubvol, oldfd, 0, NULL, NULL, xdata, NULL); + DECODE_SYNCOP_ERR(ret); + if (ret) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FSYNC_FAILED, + "err=%s", strerror(errno), "gfid=%s", + uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", + graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, + NULL); + } + } + + newinode = glfs_refresh_inode_safe(newsubvol, oldinode, _gf_false); + if (!newinode) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, + API_MSG_INODE_REFRESH_FAILED, "gfid=%s", + uuid_utoa_r(oldinode->gfid, uuid1), "err=%s", strerror(errno), + "subvol=%s", graphid_str(newsubvol), "id=%d", + newsubvol->graph->id, NULL); + goto out; + } + + newfd = fd_create(newinode, getpid()); + if (!newfd) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, + API_MSG_FDCREATE_FAILED_ON_GRAPH, "gfid=%s", + uuid_utoa_r(newinode->gfid, uuid1), "err=%s", strerror(errno), + "subvol=%s", graphid_str(newsubvol), "id=%d", + newsubvol->graph->id, NULL); + goto out; + } + + loc.inode = inode_ref(newinode); + + ret = inode_path(oldfd->inode, NULL, (char **)&loc.path); + if (ret < 0) { + gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_INODE_PATH_FAILED, NULL); + goto out; + } + + gf_uuid_copy(loc.gfid, oldinode->gfid); + + if (IA_ISDIR(oldinode->ia_type)) + ret = syncop_opendir(newsubvol, &loc, newfd, NULL, NULL); + else + ret = syncop_open(newsubvol, &loc, + oldfd->flags & ~(O_TRUNC | O_EXCL | O_CREAT), newfd, + NULL, NULL); + DECODE_SYNCOP_ERR(ret); + loc_wipe(&loc); + + if (ret) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_SYNCOP_OPEN_FAILED, + "type=%s", IA_ISDIR(oldinode->ia_type) ? "dir" : "", "gfid=%s", + uuid_utoa_r(newinode->gfid, uuid1), "err=%s", strerror(errno), + "subvol=%s", graphid_str(newsubvol), "id=%d", + newsubvol->graph->id, NULL); + goto out; + } + + ret = glfs_migrate_fd_locks_safe(fs, oldsubvol, oldfd, newsubvol, newfd); + + if (ret) { + gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_LOCK_MIGRATE_FAILED, + "gfid=%s", uuid_utoa_r(newinode->gfid, uuid1), "err=%s", + strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", + newsubvol->graph->id, NULL); + goto out; + } + + newfd->flags = oldfd->flags; + fd_bind(newfd); out: - if (newinode) - inode_unref (newinode); + if (newinode) + inode_unref(newinode); - if (ret) { - fd_unref (newfd); - newfd = NULL; - } + if (ret) { + fd_unref(newfd); + newfd = NULL; + } - return newfd; -} + if (xdata) + dict_unref(xdata); + return newfd; +} fd_t * -__glfs_migrate_fd (struct glfs *fs, xlator_t *newsubvol, struct glfs_fd *glfd) +__glfs_migrate_fd(struct glfs *fs, xlator_t *newsubvol, struct glfs_fd *glfd) { - fd_t *oldfd = NULL; - fd_t *newfd = NULL; + fd_t *oldfd = NULL; + fd_t *newfd = NULL; - oldfd = glfd->fd; + oldfd = glfd->fd; - fs->migration_in_progress = 1; - pthread_mutex_unlock (&fs->mutex); - { - newfd = glfs_migrate_fd_safe (fs, newsubvol, oldfd); - } - pthread_mutex_lock (&fs->mutex); - fs->migration_in_progress = 0; + fs->migration_in_progress = 1; + pthread_mutex_unlock(&fs->mutex); + { + newfd = glfs_migrate_fd_safe(fs, newsubvol, oldfd); + } + pthread_mutex_lock(&fs->mutex); + fs->migration_in_progress = 0; + pthread_cond_broadcast(&fs->cond); - return newfd; -} + /* wake up other waiting tasks */ + __GLFS_SYNCTASK_WAKE(fs); + return newfd; +} fd_t * -__glfs_resolve_fd (struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) +__glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) { - fd_t *fd = NULL; + fd_t *fd = NULL; - if (glfd->fd->inode->table->xl == subvol) - return fd_ref (glfd->fd); + if (glfd->fd->inode->table->xl == subvol) + return fd_ref(glfd->fd); - fd = __glfs_migrate_fd (fs, subvol, glfd); - if (!fd) - return NULL; + fd = __glfs_migrate_fd(fs, subvol, glfd); + if (!fd) + return NULL; - if (subvol == fs->active_subvol) { - fd_unref (glfd->fd); - glfd->fd = fd_ref (fd); - } + if (subvol == fs->active_subvol) { + fd_unref(glfd->fd); + glfd->fd = fd_ref(fd); + } - return fd; + return fd; } - fd_t * -glfs_resolve_fd (struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) +glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) { - fd_t *fd = NULL; + fd_t *fd = NULL; - glfs_lock (fs); - { - fd = __glfs_resolve_fd (fs, subvol, glfd); - } - glfs_unlock (fs); + glfs_lock(fs, _gf_true); + { + fd = __glfs_resolve_fd(fs, subvol, glfd); + } + glfs_unlock(fs); - return fd; + return fd; } - void -__glfs_migrate_openfds (struct glfs *fs, xlator_t *subvol) +__glfs_migrate_openfds(struct glfs *fs, xlator_t *subvol) { - struct glfs_fd *glfd = NULL; - fd_t *fd = NULL; - - list_for_each_entry (glfd, &fs->openfds, openfds) { - if (uuid_is_null (glfd->fd->inode->gfid)) { - gf_log (fs->volname, GF_LOG_INFO, - "skipping openfd %p/%p in graph %s (%d)", - glfd, glfd->fd, graphid_str(subvol), - subvol->graph->id); - /* create in progress, defer */ - continue; - } - - fd = __glfs_migrate_fd (fs, subvol, glfd); - if (fd) { - fd_unref (glfd->fd); - glfd->fd = fd; - } - } + struct glfs_fd *glfd = NULL; + fd_t *fd = NULL; + + list_for_each_entry(glfd, &fs->openfds, openfds) + { + if (gf_uuid_is_null(glfd->fd->inode->gfid)) { + gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_OPENFD_SKIPPED, + "glfd=%p", glfd, "glfd->fd=%p", glfd->fd, "subvol=%s", + graphid_str(subvol), "id=%d", subvol->graph->id, NULL); + /* create in progress, defer */ + continue; + } + + fd = __glfs_migrate_fd(fs, subvol, glfd); + if (fd) { + fd_unref(glfd->fd); + glfd->fd = fd; + } + } } - +/* Note that though it appears that this function executes under fs->mutex, + * it is not fully executed under fs->mutex. i.e. there are functions like + * __glfs_first_lookup, __glfs_refresh_inode, __glfs_migrate_openfds which + * unlocks fs->mutex before sending any network fop, and reacquire fs->mutex + * once the fop is complete. Hence the variable read from fs at the start of the + * function need not have the same value by the end of the function. + */ xlator_t * -__glfs_active_subvol (struct glfs *fs) +__glfs_active_subvol(struct glfs *fs) +{ + xlator_t *new_subvol = NULL; + int ret = -1; + inode_t *new_cwd = NULL; + + if (!fs->next_subvol) + return fs->active_subvol; + + new_subvol = fs->mip_subvol = fs->next_subvol; + fs->next_subvol = NULL; + + ret = __glfs_first_lookup(fs, new_subvol); + if (ret) { + gf_smsg(fs->volname, GF_LOG_INFO, errno, + API_MSG_FIRST_LOOKUP_GRAPH_FAILED, "subvol=%s", + graphid_str(new_subvol), "id=%d", new_subvol->graph->id, + "err=%s", strerror(errno), NULL); + return NULL; + } + + if (fs->cwd) { + new_cwd = __glfs_refresh_inode(fs, new_subvol, fs->cwd, _gf_false); + + if (!new_cwd) { + char buf1[64]; + gf_smsg(fs->volname, GF_LOG_INFO, errno, + API_MSG_CWD_GRAPH_REF_FAILED, "buf=%s", + uuid_utoa_r(fs->cwd->gfid, buf1), "subvol=%s", + graphid_str(new_subvol), "id=%d", new_subvol->graph->id, + "err=%s", strerror(errno), NULL); + return NULL; + } + } + + __glfs_migrate_openfds(fs, new_subvol); + /* TODO: Migrate the fds and inodes which have leases to the new graph + * (issue #350)*/ + + /* switching @active_subvol and @cwd + should be atomic + */ + fs->old_subvol = fs->active_subvol; + fs->active_subvol = fs->mip_subvol; + fs->mip_subvol = NULL; + + if (new_cwd) { + __glfs_cwd_set(fs, new_cwd); + inode_unref(new_cwd); + } + + gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_SWITCHED_GRAPH, "subvol=%s", + graphid_str(new_subvol), "id=%d", new_subvol->graph->id, NULL); + + return new_subvol; +} + +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_subvol_done, 3.4.0) +void +priv_glfs_subvol_done(struct glfs *fs, xlator_t *subvol) { - xlator_t *new_subvol = NULL; - int ret = -1; - inode_t *new_cwd = NULL; - - if (!fs->next_subvol) - return fs->active_subvol; - - new_subvol = fs->next_subvol; - - ret = __glfs_first_lookup (fs, new_subvol); - if (ret) { - gf_log (fs->volname, GF_LOG_INFO, - "first lookup on graph %s (%d) failed (%s)", - graphid_str (new_subvol), new_subvol->graph->id, - strerror (errno)); - return NULL; - } - - if (fs->cwd) { - new_cwd = __glfs_refresh_inode (fs, new_subvol, fs->cwd); - - if (!new_cwd) { - char buf1[64]; - gf_log (fs->volname, GF_LOG_INFO, - "cwd refresh of %s graph %s (%d) failed (%s)", - uuid_utoa_r (fs->cwd->gfid, buf1), - graphid_str (new_subvol), - new_subvol->graph->id, strerror (errno)); - return NULL; - } - } - - __glfs_migrate_openfds (fs, new_subvol); - - /* switching @active_subvol and @cwd - should be atomic - */ - fs->old_subvol = fs->active_subvol; - fs->active_subvol = fs->next_subvol; - fs->next_subvol = NULL; - - if (new_cwd) { - __glfs_cwd_set (fs, new_cwd); - inode_unref (new_cwd); - } - - gf_log (fs->volname, GF_LOG_INFO, "switched to graph %s (%d)", - graphid_str (new_subvol), new_subvol->graph->id); - - return new_subvol; + int ref = 0; + xlator_t *active_subvol = NULL; + + if (!subvol) + return; + + /* For decrementing subvol->wind ref count we need not check/wait for + * migration-in-progress flag. + * Also glfs_subvol_done is called in call-back path therefore waiting + * for migration-in-progress flag can lead to dead-lock. + */ + glfs_lock(fs, _gf_false); + { + ref = (--subvol->winds); + active_subvol = fs->active_subvol; + } + glfs_unlock(fs); + + if (ref == 0) { + assert(subvol != active_subvol); + xlator_notify(subvol, GF_EVENT_PARENT_DOWN, subvol, NULL); + } } +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_active_subvol, 3.4.0) xlator_t * -glfs_active_subvol (struct glfs *fs) +priv_glfs_active_subvol(struct glfs *fs) { - xlator_t *subvol = NULL; - xlator_t *old_subvol = NULL; + xlator_t *subvol = NULL; + xlator_t *old_subvol = NULL; - glfs_lock (fs); - { - subvol = __glfs_active_subvol (fs); + glfs_lock(fs, _gf_true); + { + subvol = __glfs_active_subvol(fs); - if (subvol) - subvol->winds++; + if (subvol) + subvol->winds++; - if (fs->old_subvol) { - old_subvol = fs->old_subvol; - fs->old_subvol = NULL; - old_subvol->switched = 1; - } - } - glfs_unlock (fs); + if (fs->old_subvol) { + old_subvol = fs->old_subvol; + fs->old_subvol = NULL; + old_subvol->switched = 1; + } + } + glfs_unlock(fs); - if (old_subvol) - glfs_subvol_done (fs, old_subvol); + if (old_subvol) + priv_glfs_subvol_done(fs, old_subvol); - return subvol; + return subvol; } - -void -glfs_subvol_done (struct glfs *fs, xlator_t *subvol) +int +__glfs_cwd_set(struct glfs *fs, inode_t *inode) { - int ref = 0; - xlator_t *active_subvol = NULL; - - glfs_lock (fs); - { - ref = (--subvol->winds); - active_subvol = fs->active_subvol; - } - glfs_unlock (fs); - - if (ref == 0) { - assert (subvol != active_subvol); - xlator_notify (subvol, GF_EVENT_PARENT_DOWN, subvol, NULL); - } -} + if (inode->table->xl != fs->active_subvol) { + inode = __glfs_refresh_inode(fs, fs->active_subvol, inode, _gf_false); + if (!inode) + return -1; + } else { + inode_ref(inode); + } + + if (fs->cwd) + inode_unref(fs->cwd); + fs->cwd = inode; + + return 0; +} int -__glfs_cwd_set (struct glfs *fs, inode_t *inode) +glfs_cwd_set(struct glfs *fs, inode_t *inode) { - if (inode->table->xl != fs->active_subvol) { - inode = __glfs_refresh_inode (fs, fs->active_subvol, inode); - if (!inode) - return -1; - } else { - inode_ref (inode); - } - - if (fs->cwd) - inode_unref (fs->cwd); + int ret = 0; - fs->cwd = inode; + glfs_lock(fs, _gf_true); + { + ret = __glfs_cwd_set(fs, inode); + } + glfs_unlock(fs); - return 0; + return ret; } - -int -glfs_cwd_set (struct glfs *fs, inode_t *inode) +inode_t * +__glfs_cwd_get(struct glfs *fs) { - int ret = 0; + inode_t *cwd = NULL; - glfs_lock (fs); - { - ret = __glfs_cwd_set (fs, inode); - } - glfs_unlock (fs); + if (!fs->cwd) + return NULL; - return ret; + if (fs->cwd->table->xl == fs->active_subvol) { + cwd = inode_ref(fs->cwd); + return cwd; + } + + cwd = __glfs_refresh_inode(fs, fs->active_subvol, fs->cwd, _gf_false); + + return cwd; } +inode_t * +glfs_cwd_get(struct glfs *fs) +{ + inode_t *cwd = NULL; + + glfs_lock(fs, _gf_true); + { + cwd = __glfs_cwd_get(fs); + } + glfs_unlock(fs); + + return cwd; +} inode_t * -__glfs_cwd_get (struct glfs *fs) +__glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, + struct glfs_object *object) { - inode_t *cwd = NULL; + inode_t *inode = NULL; + gf_boolean_t lookup_needed = _gf_false; + + lookup_needed = inode_needs_lookup(object->inode, THIS); - if (!fs->cwd) - return NULL; + if (!lookup_needed && object->inode->table->xl == subvol) + return inode_ref(object->inode); - if (fs->cwd->table->xl == fs->active_subvol) { - cwd = inode_ref (fs->cwd); - return cwd; - } + inode = __glfs_refresh_inode(fs, fs->active_subvol, object->inode, + lookup_needed); + if (!inode) + return NULL; - cwd = __glfs_refresh_inode (fs, fs->active_subvol, fs->cwd); + if (subvol == fs->active_subvol) { + inode_unref(object->inode); + object->inode = inode_ref(inode); + } - return cwd; + return inode; } inode_t * -glfs_cwd_get (struct glfs *fs) +glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, + struct glfs_object *object) +{ + inode_t *inode = NULL; + + glfs_lock(fs, _gf_true); + { + inode = __glfs_resolve_inode(fs, subvol, object); + } + glfs_unlock(fs); + + return inode; +} + +int +glfs_create_object(loc_t *loc, struct glfs_object **retobject) { - inode_t *cwd = NULL; + struct glfs_object *object = NULL; + + object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); + if (object == NULL) { + errno = ENOMEM; + return -1; + } + + object->inode = loc->inode; + gf_uuid_copy(object->gfid, object->inode->gfid); + + /* we hold the reference */ + loc->inode = NULL; - glfs_lock (fs); - { - cwd = __glfs_cwd_get (fs); - } - glfs_unlock (fs); + *retobject = object; - return cwd; + return 0; +} + +struct glfs_object * +glfs_h_resolve_symlink(struct glfs *fs, struct glfs_object *object) +{ + xlator_t *subvol = NULL; + loc_t sym_loc = { + 0, + }; + struct iatt iatt = { + 0, + }; + char *lpath = NULL; + int ret = 0; + struct glfs_object *target_object = NULL; + + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + ret = glfs_resolve_symlink(fs, subvol, object->inode, &lpath); + if (ret < 0) + goto out; + + ret = glfs_resolve_at(fs, subvol, NULL, lpath, &sym_loc, &iatt, + /* always recurisvely follow while + following symlink + */ + 1, 0); + if (ret == 0) + ret = glfs_create_object(&sym_loc, &target_object); + +out: + loc_wipe(&sym_loc); + GF_FREE(lpath); + return target_object; } diff --git a/api/src/glfs.c b/api/src/glfs.c index efda6b67eae..b4bf1423f6d 100644 --- a/api/src/glfs.c +++ b/api/src/glfs.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,10 +8,8 @@ cases as published by the Free Software Foundation. */ - /* TODO: - - merge locks in glfs_posix_lock for lock self-healing - set proper pid/lk_owner to call frames (currently buried in syncop) - fix logging.c/h to store logfp and loglevel in glusterfs_ctx_t and reach it via THIS. @@ -20,7 +18,6 @@ - handle SEEK_END failure in _lseek() - handle umask (per filesystem?) - make itables LRU based - - implement glfs_fini() - 0-copy for readv/writev - reconcile the open/creat mess */ @@ -33,568 +30,1777 @@ #include <sys/types.h> #include <unistd.h> #include <limits.h> - -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" +#ifdef GF_LINUX_HOST_OS +#include <sys/prctl.h> #endif -#include "glusterfs.h" -#include "logging.h" -#include "stack.h" -#include "event.h" +#include <glusterfs/glusterfs.h> +#include <glusterfs/logging.h> +#include <glusterfs/stack.h> +#include <glusterfs/gf-event.h> #include "glfs-mem-types.h" -#include "common-utils.h" -#include "syncop.h" -#include "call-stub.h" - +#include <glusterfs/common-utils.h> +#include <glusterfs/syncop.h> +#include <glusterfs/call-stub.h> +#include <glusterfs/hashfn.h> +#include "rpc-clnt.h" +#include <glusterfs/statedump.h> +#include <glusterfs/syscall.h> + +#include "gfapi-messages.h" #include "glfs.h" #include "glfs-internal.h" -#include "hashfn.h" - static gf_boolean_t -vol_assigned (cmd_args_t *args) +vol_assigned(cmd_args_t *args) { - return args->volfile || args->volfile_server; + return args->volfile || args->volfile_server; } - static int -glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) -{ - call_pool_t *pool = NULL; - int ret = -1; - - xlator_mem_acct_init (THIS, glfs_mt_end); - - ctx->process_uuid = generate_glusterfs_ctx_id (); - if (!ctx->process_uuid) { - goto err; - } - - ctx->page_size = 128 * GF_UNIT_KB; - - ctx->iobuf_pool = iobuf_pool_new (); - if (!ctx->iobuf_pool) { - goto err; - } - - ctx->event_pool = event_pool_new (DEFAULT_EVENT_POOL_SIZE); - if (!ctx->event_pool) { - goto err; - } - - ctx->env = syncenv_new (0); - if (!ctx->env) { - goto err; - } - - pool = GF_CALLOC (1, sizeof (call_pool_t), - glfs_mt_call_pool_t); - if (!pool) { - goto err; - } - - /* frame_mem_pool size 112 * 4k */ - pool->frame_mem_pool = mem_pool_new (call_frame_t, 4096); - if (!pool->frame_mem_pool) { - goto err; - } - /* stack_mem_pool size 256 * 1024 */ - pool->stack_mem_pool = mem_pool_new (call_stack_t, 1024); - if (!pool->stack_mem_pool) { - goto err; - } - - ctx->stub_mem_pool = mem_pool_new (call_stub_t, 1024); - if (!ctx->stub_mem_pool) { - goto err; - } - - ctx->dict_pool = mem_pool_new (dict_t, GF_MEMPOOL_COUNT_OF_DICT_T); - if (!ctx->dict_pool) - goto err; - - ctx->dict_pair_pool = mem_pool_new (data_pair_t, - GF_MEMPOOL_COUNT_OF_DATA_PAIR_T); - if (!ctx->dict_pair_pool) - goto err; - - ctx->dict_data_pool = mem_pool_new (data_t, GF_MEMPOOL_COUNT_OF_DATA_T); - if (!ctx->dict_data_pool) - goto err; - - INIT_LIST_HEAD (&pool->all_frames); - INIT_LIST_HEAD (&ctx->cmd_args.xlator_options); - LOCK_INIT (&pool->lock); - ctx->pool = pool; - - pthread_mutex_init (&(ctx->lock), NULL); - - ret = 0; +glusterfs_ctx_defaults_init(glusterfs_ctx_t *ctx) +{ + call_pool_t *pool = NULL; + int ret = -1; + + if (!ctx) { + goto err; + } + + ret = xlator_mem_acct_init(THIS, glfs_mt_end + 1); + if (ret != 0) { + gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_MEM_ACCT_INIT_FAILED, + NULL); + return ret; + } + + /* reset ret to -1 so that we don't need to explicitly + * set it in all error paths before "goto err" + */ + + ret = -1; + + ctx->process_uuid = generate_glusterfs_ctx_id(); + if (!ctx->process_uuid) { + goto err; + } + + ctx->page_size = 128 * GF_UNIT_KB; + + ctx->iobuf_pool = iobuf_pool_new(); + if (!ctx->iobuf_pool) { + goto err; + } + + ctx->event_pool = gf_event_pool_new(DEFAULT_EVENT_POOL_SIZE, + STARTING_EVENT_THREADS); + if (!ctx->event_pool) { + goto err; + } + + ctx->env = syncenv_new(0, 0, 0); + if (!ctx->env) { + goto err; + } + + pool = GF_CALLOC(1, sizeof(call_pool_t), glfs_mt_call_pool_t); + if (!pool) { + goto err; + } + + /* frame_mem_pool size 112 * 4k */ + pool->frame_mem_pool = mem_pool_new(call_frame_t, 4096); + if (!pool->frame_mem_pool) { + goto err; + } + /* stack_mem_pool size 256 * 1024 */ + pool->stack_mem_pool = mem_pool_new(call_stack_t, 1024); + if (!pool->stack_mem_pool) { + goto err; + } + + ctx->stub_mem_pool = mem_pool_new(call_stub_t, 1024); + if (!ctx->stub_mem_pool) { + goto err; + } + + ctx->dict_pool = mem_pool_new(dict_t, GF_MEMPOOL_COUNT_OF_DICT_T); + if (!ctx->dict_pool) + goto err; + + ctx->dict_pair_pool = mem_pool_new(data_pair_t, + GF_MEMPOOL_COUNT_OF_DATA_PAIR_T); + if (!ctx->dict_pair_pool) + goto err; + + ctx->dict_data_pool = mem_pool_new(data_t, GF_MEMPOOL_COUNT_OF_DATA_T); + if (!ctx->dict_data_pool) + goto err; + + ctx->logbuf_pool = mem_pool_new(log_buf_t, GF_MEMPOOL_COUNT_OF_LRU_BUF_T); + if (!ctx->logbuf_pool) + goto err; + + INIT_LIST_HEAD(&pool->all_frames); + INIT_LIST_HEAD(&ctx->cmd_args.xlator_options); + INIT_LIST_HEAD(&ctx->cmd_args.volfile_servers); + + LOCK_INIT(&pool->lock); + ctx->pool = pool; + + ret = 0; err: - if (ret && pool) { - if (pool->frame_mem_pool) - mem_pool_destroy (pool->frame_mem_pool); - if (pool->stack_mem_pool) - mem_pool_destroy (pool->stack_mem_pool); - GF_FREE (pool); - } - - if (ret && ctx) { - if (ctx->stub_mem_pool) - mem_pool_destroy (ctx->stub_mem_pool); - if (ctx->dict_pool) - mem_pool_destroy (ctx->dict_pool); - if (ctx->dict_data_pool) - mem_pool_destroy (ctx->dict_data_pool); - if (ctx->dict_pair_pool) - mem_pool_destroy (ctx->dict_pair_pool); - } - - return ret; + if (ret && pool) { + if (pool->frame_mem_pool) + mem_pool_destroy(pool->frame_mem_pool); + if (pool->stack_mem_pool) + mem_pool_destroy(pool->stack_mem_pool); + GF_FREE(pool); + } + + if (ret && ctx) { + if (ctx->stub_mem_pool) + mem_pool_destroy(ctx->stub_mem_pool); + if (ctx->dict_pool) + mem_pool_destroy(ctx->dict_pool); + if (ctx->dict_data_pool) + mem_pool_destroy(ctx->dict_data_pool); + if (ctx->dict_pair_pool) + mem_pool_destroy(ctx->dict_pair_pool); + if (ctx->logbuf_pool) + mem_pool_destroy(ctx->logbuf_pool); + } + + return ret; } - static int -create_master (struct glfs *fs) +create_master(struct glfs *fs) { - int ret = 0; - xlator_t *master = NULL; - - master = GF_CALLOC (1, sizeof (*master), - glfs_mt_xlator_t); - if (!master) - goto err; + int ret = 0; + xlator_t *master = NULL; - master->name = gf_strdup ("gfapi"); - if (!master->name) - goto err; + master = GF_CALLOC(1, sizeof(*master), glfs_mt_xlator_t); + if (!master) + goto err; - if (xlator_set_type (master, "mount/api") == -1) { - gf_log ("glfs", GF_LOG_ERROR, - "master xlator for %s initialization failed", - fs->volname); - goto err; - } + master->name = gf_strdup("gfapi"); + if (!master->name) + goto err; - master->ctx = fs->ctx; - master->private = fs; - master->options = get_new_dict (); - if (!master->options) - goto err; + if (xlator_set_type(master, "mount/api") == -1) { + gf_smsg("glfs", GF_LOG_ERROR, 0, API_MSG_MASTER_XLATOR_INIT_FAILED, + "name=%s", fs->volname, NULL); + goto err; + } + master->ctx = fs->ctx; + master->private = fs; + master->options = dict_new(); + if (!master->options) + goto err; - ret = xlator_init (master); - if (ret) { - gf_log ("glfs", GF_LOG_ERROR, - "failed to initialize gfapi translator"); - goto err; - } + ret = xlator_init(master); + if (ret) { + gf_smsg("glfs", GF_LOG_ERROR, 0, API_MSG_GFAPI_XLATOR_INIT_FAILED, + NULL); + goto err; + } - fs->ctx->master = master; - THIS = master; + fs->ctx->master = master; + THIS = master; - return 0; + return 0; err: - if (master) { - xlator_destroy (master); - } + if (master) { + xlator_destroy(master); + } - return -1; + return -1; } - static FILE * -get_volfp (struct glfs *fs) +get_volfp(struct glfs *fs) { - int ret = 0; - cmd_args_t *cmd_args = NULL; - FILE *specfp = NULL; - struct stat statbuf; - - cmd_args = &fs->ctx->cmd_args; + cmd_args_t *cmd_args = NULL; + FILE *specfp = NULL; - ret = lstat (cmd_args->volfile, &statbuf); - if (ret == -1) { - gf_log ("glfs", GF_LOG_ERROR, - "%s: %s", cmd_args->volfile, strerror (errno)); - return NULL; - } + cmd_args = &fs->ctx->cmd_args; - if ((specfp = fopen (cmd_args->volfile, "r")) == NULL) { - gf_log ("glfs", GF_LOG_ERROR, - "volume file %s: %s", - cmd_args->volfile, - strerror (errno)); - return NULL; - } + if ((specfp = fopen(cmd_args->volfile, "r")) == NULL) { + gf_smsg("glfs", GF_LOG_ERROR, errno, API_MSG_VOLFILE_OPEN_FAILED, + "file=%s", cmd_args->volfile, "err=%s", strerror(errno), NULL); + return NULL; + } - gf_log ("glfs", GF_LOG_DEBUG, - "loading volume file %s", cmd_args->volfile); + gf_msg_debug("glfs", 0, "loading volume file %s", cmd_args->volfile); - return specfp; + return specfp; } - int -glfs_volumes_init (struct glfs *fs) +glfs_volumes_init(struct glfs *fs) { - FILE *fp = NULL; - cmd_args_t *cmd_args = NULL; - int ret = 0; + FILE *fp = NULL; + cmd_args_t *cmd_args = NULL; + int ret = 0; - cmd_args = &fs->ctx->cmd_args; + cmd_args = &fs->ctx->cmd_args; - if (!vol_assigned (cmd_args)) - return -1; + if (!vol_assigned(cmd_args)) + return -1; - if (cmd_args->volfile_server) { - ret = glfs_mgmt_init (fs); - goto out; - } + if (sys_access(SECURE_ACCESS_FILE, F_OK) == 0) { + fs->ctx->secure_mgmt = 1; + fs->ctx->ssl_cert_depth = glusterfs_read_secure_access_file(); + } - fp = get_volfp (fs); + if (cmd_args->volfile_server) { + ret = glfs_mgmt_init(fs); + goto out; + } - if (!fp) { - gf_log ("glfs", GF_LOG_ERROR, - "Cannot reach volume specification file"); - ret = -1; - goto out; - } + fp = get_volfp(fs); - ret = glfs_process_volfp (fs, fp); - if (ret) - goto out; + if (!fp) { + gf_smsg("glfs", GF_LOG_ERROR, ENOENT, API_MSG_VOL_SPEC_FILE_ERROR, + NULL); + ret = -1; + goto out; + } + + ret = glfs_process_volfp(fs, fp); + if (ret) + goto out; out: - return ret; + return ret; } - /////////////////////////////////////////////////////////////////////////////// - +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_xlator_option, 3.4.0) int -glfs_set_xlator_option (struct glfs *fs, const char *xlator, const char *key, - const char *value) +pub_glfs_set_xlator_option(struct glfs *fs, const char *xlator, const char *key, + const char *value) { - xlator_cmdline_option_t *option = NULL; + xlator_cmdline_option_t *option = NULL; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); - option = GF_CALLOC (1, sizeof (*option), - glfs_mt_xlator_cmdline_option_t); - if (!option) - goto enomem; + option = GF_CALLOC(1, sizeof(*option), glfs_mt_xlator_cmdline_option_t); + if (!option) + goto enomem; - INIT_LIST_HEAD (&option->cmd_args); + INIT_LIST_HEAD(&option->cmd_args); - option->volume = gf_strdup (xlator); - if (!option->volume) - goto enomem; - option->key = gf_strdup (key); - if (!option->key) - goto enomem; - option->value = gf_strdup (value); - if (!option->value) - goto enomem; + option->volume = gf_strdup(xlator); + if (!option->volume) + goto enomem; + option->key = gf_strdup(key); + if (!option->key) + goto enomem; + option->value = gf_strdup(value); + if (!option->value) + goto enomem; - list_add (&option->cmd_args, &fs->ctx->cmd_args.xlator_options); + list_add(&option->cmd_args, &fs->ctx->cmd_args.xlator_options); - return 0; + __GLFS_EXIT_FS; + + return 0; enomem: - errno = ENOMEM; + errno = ENOMEM; + + if (!option) { + __GLFS_EXIT_FS; + return -1; + } - if (!option) - return -1; + GF_FREE(option->volume); + GF_FREE(option->key); + GF_FREE(option->value); + GF_FREE(option); - GF_FREE (option->volume); - GF_FREE (option->key); - GF_FREE (option->value); - GF_FREE (option); + __GLFS_EXIT_FS; - return -1; +invalid_fs: + return -1; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_unset_volfile_server, 3.5.1) +int +pub_glfs_unset_volfile_server(struct glfs *fs, const char *transport, + const char *host, const int port) +{ + cmd_args_t *cmd_args = NULL; + server_cmdline_t *server = NULL; + server_cmdline_t *tmp = NULL; + char *transport_val = NULL; + int port_val = 0; + int ret = -1; + + if (!fs || !host) { + errno = EINVAL; + return ret; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + cmd_args = &fs->ctx->cmd_args; + + if (transport) { + transport_val = gf_strdup(transport); + } else { + transport_val = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); + } + + if (!transport_val) { + errno = ENOMEM; + goto out; + } + + if (port) { + port_val = port; + } else { + port_val = GF_DEFAULT_BASE_PORT; + } + + list_for_each_entry_safe(server, tmp, &cmd_args->curr_server->list, list) + { + if (!server->volfile_server || !server->transport) + continue; + if ((!strcmp(server->volfile_server, host) && + !strcmp(server->transport, transport_val) && + (server->port == port_val))) { + list_del(&server->list); + ret = 0; + goto out; + } + } -struct glfs * -glfs_from_glfd (struct glfs_fd *glfd) +out: + GF_FREE(transport_val); + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_volfile_server, 3.4.0) +int +pub_glfs_set_volfile_server(struct glfs *fs, const char *transport, + const char *host, int port) { - return glfd->fs; + cmd_args_t *cmd_args = NULL; + int ret = -1; + char *server_host = NULL; + char *server_transport = NULL; + + if (!fs || !host) { + errno = EINVAL; + return ret; + } + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + cmd_args = &fs->ctx->cmd_args; + cmd_args->max_connect_attempts = 1; + + server_host = gf_strdup(host); + if (!server_host) { + errno = ENOMEM; + goto out; + } + + if (transport) { + /* volfile fetch support over tcp|unix only */ + if (!strcmp(transport, "tcp") || !strcmp(transport, "unix")) { + server_transport = gf_strdup(transport); + } else if (!strcmp(transport, "rdma")) { + server_transport = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); + gf_smsg("glfs", GF_LOG_WARNING, EINVAL, API_MSG_TRANS_RDMA_DEP, + NULL); + } else { + gf_smsg("glfs", GF_LOG_TRACE, EINVAL, API_MSG_TRANS_NOT_SUPPORTED, + "transport=%s", transport, NULL); + goto out; + } + } else { + server_transport = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); + } + + if (!server_transport) { + errno = ENOMEM; + goto out; + } + + if (!port) { + port = GF_DEFAULT_BASE_PORT; + } + + if (!strcmp(server_transport, "unix")) { + port = 0; + } + + ret = gf_set_volfile_server_common(cmd_args, server_host, server_transport, + port); + if (ret) { + gf_log("glfs", GF_LOG_ERROR, "failed to set volfile server: %s", + strerror(errno)); + } + +out: + if (server_host) { + GF_FREE(server_host); + } + + if (server_transport) { + GF_FREE(server_transport); + } + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; } +/* * + * Used to free the arguments allocated by glfs_set_volfile_server() + */ +static void +glfs_free_volfile_servers(cmd_args_t *cmd_args) +{ + server_cmdline_t *server = NULL; + server_cmdline_t *tmp = NULL; + + GF_VALIDATE_OR_GOTO(THIS->name, cmd_args, out); + + list_for_each_entry_safe(server, tmp, &cmd_args->volfile_servers, list) + { + list_del_init(&server->list); + GF_FREE(server->volfile_server); + GF_FREE(server->transport); + GF_FREE(server); + } + cmd_args->curr_server = NULL; +out: + return; +} -struct glfs_fd * -glfs_fd_new (struct glfs *fs) +static void +glfs_free_xlator_options(cmd_args_t *cmd_args) +{ + xlator_cmdline_option_t *xo = NULL; + xlator_cmdline_option_t *tmp_xo = NULL; + + if (!&(cmd_args->xlator_options)) + return; + + list_for_each_entry_safe(xo, tmp_xo, &cmd_args->xlator_options, cmd_args) + { + list_del_init(&xo->cmd_args); + GF_FREE(xo->volume); + GF_FREE(xo->key); + GF_FREE(xo->value); + GF_FREE(xo); + } +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsuid, 3.4.2) +int +pub_glfs_setfsuid(uid_t fsuid) { - struct glfs_fd *glfd = NULL; + /* TODO: + * - Set the THIS and restore it appropriately + */ + return syncopctx_setfsuid(&fsuid); +} - glfd = GF_CALLOC (1, sizeof (*glfd), glfs_mt_glfs_fd_t); - if (!glfd) - return NULL; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsgid, 3.4.2) +int +pub_glfs_setfsgid(gid_t fsgid) +{ + /* TODO: + * - Set the THIS and restore it appropriately + */ + return syncopctx_setfsgid(&fsgid); +} - glfd->fs = fs; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsgroups, 3.4.2) +int +pub_glfs_setfsgroups(size_t size, const gid_t *list) +{ + /* TODO: + * - Set the THIS and restore it appropriately + */ + return syncopctx_setfsgroups(size, list); +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsleaseid, 4.0.0) +int +pub_glfs_setfsleaseid(glfs_leaseid_t leaseid) +{ + int ret = -1; + char *gleaseid = NULL; + + gleaseid = gf_leaseid_get(); + if (gleaseid) { + if (leaseid) + memcpy(gleaseid, leaseid, LEASE_ID_SIZE); + else /* reset leaseid */ + memset(gleaseid, 0, LEASE_ID_SIZE); + ret = 0; + } + + if (ret) + gf_log("glfs", GF_LOG_ERROR, "failed to set leaseid: %s", + strerror(errno)); + return ret; +} - INIT_LIST_HEAD (&glfd->openfds); +int +get_fop_attr_glfd(dict_t **fop_attr, struct glfs_fd *glfd) +{ + char *leaseid = NULL; + int ret = 0; + gf_boolean_t dict_create = _gf_false; + + leaseid = GF_MALLOC(LEASE_ID_SIZE, gf_common_mt_char); + GF_CHECK_ALLOC_AND_LOG("gfapi", leaseid, ret, "lease id alloc failed", out); + memcpy(leaseid, glfd->lease_id, LEASE_ID_SIZE); + if (*fop_attr == NULL) { + *fop_attr = dict_new(); + dict_create = _gf_true; + } + GF_CHECK_ALLOC_AND_LOG("gfapi", *fop_attr, ret, "dict_new failed", out); + ret = dict_set_bin(*fop_attr, "lease-id", leaseid, LEASE_ID_SIZE); +out: + if (ret) { + GF_FREE(leaseid); + if (dict_create) { + if (*fop_attr) + dict_unref(*fop_attr); + *fop_attr = NULL; + } + } + return ret; +} - return glfd; +int +set_fop_attr_glfd(struct glfs_fd *glfd) +{ + char *lease_id = NULL; + int ret = -1; + + lease_id = gf_existing_leaseid(); + if (lease_id) { + memcpy(glfd->lease_id, lease_id, LEASE_ID_SIZE); + ret = 0; + } + return ret; } +int +get_fop_attr_thrd_key(dict_t **fop_attr) +{ + char *existing_leaseid = NULL, *leaseid = NULL; + int ret = 0; + gf_boolean_t dict_create = _gf_false; + + existing_leaseid = gf_existing_leaseid(); + if (existing_leaseid) { + leaseid = GF_MALLOC(LEASE_ID_SIZE, gf_common_mt_char); + GF_CHECK_ALLOC_AND_LOG("gfapi", leaseid, ret, "lease id alloc failed", + out); + memcpy(leaseid, existing_leaseid, LEASE_ID_SIZE); + if (*fop_attr == NULL) { + *fop_attr = dict_new(); + dict_create = _gf_true; + } + GF_CHECK_ALLOC_AND_LOG("gfapi", *fop_attr, ret, "dict_new failed", out); + ret = dict_set_bin(*fop_attr, "lease-id", leaseid, LEASE_ID_SIZE); + } + +out: + if (ret) { + GF_FREE(leaseid); + if (dict_create) { + if (*fop_attr) + dict_unref(*fop_attr); + *fop_attr = NULL; + } + } + return ret; +} void -glfs_fd_bind (struct glfs_fd *glfd) +unset_fop_attr(dict_t **fop_attr) +{ + char *lease_id = NULL; + lease_id = gf_existing_leaseid(); + if (lease_id) + memset(lease_id, 0, LEASE_ID_SIZE); + if (*fop_attr) { + dict_unref(*fop_attr); + *fop_attr = NULL; + } +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_from_glfd, 3.4.0) +struct glfs * +pub_glfs_from_glfd(struct glfs_fd *glfd) { - struct glfs *fs = NULL; + if (glfd == NULL) { + errno = EBADF; + return NULL; + } + + return glfd->fs; +} + +static void +glfs_fd_destroy(struct glfs_fd *glfd) +{ + if (!glfd) + return; + + glfs_lock(glfd->fs, _gf_true); + { + list_del_init(&glfd->openfds); + } + glfs_unlock(glfd->fs); + + if (glfd->fd) { + fd_unref(glfd->fd); + glfd->fd = NULL; + } - fs = glfd->fs; + GF_FREE(glfd->readdirbuf); - glfs_lock (fs); - { - list_add_tail (&glfd->openfds, &fs->openfds); - } - glfs_unlock (fs); + GF_FREE(glfd); } -void -glfs_fd_destroy (struct glfs_fd *glfd) +struct glfs_fd * +glfs_fd_new(struct glfs *fs) { - if (!glfd) - return; + struct glfs_fd *glfd = NULL; + + glfd = GF_CALLOC(1, sizeof(*glfd), glfs_mt_glfs_fd_t); + if (!glfd) + return NULL; - glfs_lock (glfd->fs); - { - list_del_init (&glfd->openfds); - } - glfs_unlock (glfd->fs); + glfd->fs = fs; - if (glfd->fd) - fd_unref (glfd->fd); - GF_FREE (glfd); + INIT_LIST_HEAD(&glfd->openfds); + + GF_REF_INIT(glfd, glfs_fd_destroy); + + return glfd; } +void +glfs_fd_bind(struct glfs_fd *glfd) +{ + struct glfs *fs = NULL; + + fs = glfd->fs; + + glfs_lock(fs, _gf_true); + { + list_add_tail(&glfd->openfds, &fs->openfds); + } + glfs_unlock(fs); +} static void * -glfs_poller (void *data) +glfs_poller(void *data) { - struct glfs *fs = NULL; + struct glfs *fs = NULL; + + fs = data; + + gf_event_dispatch(fs->ctx->event_pool); + + return NULL; +} + +static struct glfs * +glfs_new_fs(const char *volname) +{ + struct glfs *fs = NULL; + + fs = CALLOC(1, sizeof(*fs)); + if (!fs) + return NULL; + + INIT_LIST_HEAD(&fs->openfds); + INIT_LIST_HEAD(&fs->upcall_list); + INIT_LIST_HEAD(&fs->waitq); + + PTHREAD_MUTEX_INIT(&fs->mutex, NULL, fs->pthread_flags, GLFS_INIT_MUTEX, + err); - fs = data; + PTHREAD_COND_INIT(&fs->cond, NULL, fs->pthread_flags, GLFS_INIT_COND, err); - event_dispatch (fs->ctx->event_pool); + PTHREAD_COND_INIT(&fs->child_down_cond, NULL, fs->pthread_flags, + GLFS_INIT_COND_CHILD, err); - return NULL; + PTHREAD_MUTEX_INIT(&fs->upcall_list_mutex, NULL, fs->pthread_flags, + GLFS_INIT_MUTEX_UPCALL, err); + + fs->volname = strdup(volname); + if (!fs->volname) + goto err; + + fs->pin_refcnt = 0; + fs->upcall_events = 0; + fs->up_cbk = NULL; + fs->up_data = NULL; + + return fs; + +err: + glfs_free_from_ctx(fs); + return NULL; } +extern xlator_t global_xlator; +extern glusterfs_ctx_t *global_ctx; +extern pthread_mutex_t global_ctx_mutex; +static int +glfs_init_global_ctx() +{ + int ret = 0; + glusterfs_ctx_t *ctx = NULL; + + pthread_mutex_lock(&global_ctx_mutex); + { + if (global_xlator.ctx) + goto unlock; + + ctx = glusterfs_ctx_new(); + if (!ctx) { + ret = -1; + goto unlock; + } + + gf_log_globals_init(ctx, GF_LOG_NONE); + + global_ctx = ctx; + global_xlator.ctx = global_ctx; + + ret = glusterfs_ctx_defaults_init(ctx); + if (ret) { + global_ctx = NULL; + global_xlator.ctx = NULL; + goto unlock; + } + } +unlock: + pthread_mutex_unlock(&global_ctx_mutex); + + if (ret) + FREE(ctx); + + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_new, 3.4.0) struct glfs * -glfs_new (const char *volname) +pub_glfs_new(const char *volname) { - struct glfs *fs = NULL; - int ret = -1; - glusterfs_ctx_t *ctx = NULL; + if (!volname) { + errno = EINVAL; + return NULL; + } + + struct glfs *fs = NULL; + int i = 0; + int ret = -1; + glusterfs_ctx_t *ctx = NULL; + xlator_t *old_THIS = NULL; + char pname[16] = ""; + char msg[32] = ""; + + if (volname[0] == '/' || volname[0] == '-') { + if (strncmp(volname, "/snaps/", 7) == 0) { + goto label; + } + errno = EINVAL; + return NULL; + } + + for (i = 0; i < strlen(volname); i++) { + if (!isalnum(volname[i]) && (volname[i] != '_') && + (volname[i] != '-')) { + errno = EINVAL; + return NULL; + } + } + +label: + /* + * Do this as soon as possible in case something else depends on + * pool allocations. + */ + mem_pools_init(); + + fs = glfs_new_fs(volname); + if (!fs) + goto out; + + ctx = glusterfs_ctx_new(); + if (!ctx) + goto out; + + /* first globals init, for gf_mem_acct_enable_set () */ + + ret = glusterfs_globals_init(ctx); + if (ret) + goto out; + + old_THIS = THIS; + ret = glfs_init_global_ctx(); + if (ret) + goto out; + + /* then ctx_defaults_init, for xlator_mem_acct_init(THIS) */ + + ret = glusterfs_ctx_defaults_init(ctx); + if (ret) + goto out; + + fs->ctx = ctx; + fs->ctx->process_mode = GF_CLIENT_PROCESS; + + ret = glfs_set_logging(fs, "/dev/null", 0); + if (ret) + goto out; + + fs->ctx->cmd_args.volfile_id = gf_strdup(volname); + if (!(fs->ctx->cmd_args.volfile_id)) { + ret = -1; + goto out; + } + + ret = -1; +#ifdef GF_LINUX_HOST_OS + ret = prctl(PR_GET_NAME, (unsigned long)pname, 0, 0, 0); +#endif + if (ret) + fs->ctx->cmd_args.process_name = gf_strdup("gfapi"); + else { + snprintf(msg, sizeof(msg), "gfapi.%s", pname); + fs->ctx->cmd_args.process_name = gf_strdup(msg); + } + ret = 0; - ctx = glusterfs_ctx_new (); - if (!ctx) { - return NULL; - } +out: + if (ret) { + if (fs) { + glfs_fini(fs); + fs = NULL; + } else { + /* glfs_fini() calls mem_pools_fini() too */ + mem_pools_fini(); + } + } + + if (old_THIS) + THIS = old_THIS; + + return fs; +} -#ifdef DEBUG - gf_mem_acct_enable_set (ctx); -#endif +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_new_from_ctx, 3.7.0) +struct glfs * +priv_glfs_new_from_ctx(glusterfs_ctx_t *ctx) +{ + struct glfs *fs = NULL; - /* first globals init, for gf_mem_acct_enable_set () */ - ret = glusterfs_globals_init (ctx); - if (ret) - return NULL; + if (!ctx) + goto out; - THIS->ctx = ctx; + fs = glfs_new_fs(""); + if (!fs) + goto out; - /* then ctx_defaults_init, for xlator_mem_acct_init(THIS) */ - ret = glusterfs_ctx_defaults_init (ctx); - if (ret) - return NULL; + fs->ctx = ctx; - fs = GF_CALLOC (1, sizeof (*fs), glfs_mt_glfs_t); - if (!fs) - return NULL; - fs->ctx = ctx; +out: + return fs; +} + +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_free_from_ctx, 3.7.0) +void +priv_glfs_free_from_ctx(struct glfs *fs) +{ + upcall_entry *u_list = NULL; + upcall_entry *tmp = NULL; - glfs_set_logging (fs, "/dev/null", 0); + if (!fs) + return; - fs->ctx->cmd_args.volfile_id = gf_strdup (volname); + /* cleanup upcall structures */ + list_for_each_entry_safe(u_list, tmp, &fs->upcall_list, upcall_list) + { + list_del_init(&u_list->upcall_list); + GF_FREE(u_list->upcall_data.data); + GF_FREE(u_list); + } - fs->volname = gf_strdup (volname); + PTHREAD_MUTEX_DESTROY(&fs->mutex, fs->pthread_flags, GLFS_INIT_MUTEX); - pthread_mutex_init (&fs->mutex, NULL); - pthread_cond_init (&fs->cond, NULL); + PTHREAD_COND_DESTROY(&fs->cond, fs->pthread_flags, GLFS_INIT_COND); - INIT_LIST_HEAD (&fs->openfds); + PTHREAD_COND_DESTROY(&fs->child_down_cond, fs->pthread_flags, + GLFS_INIT_COND_CHILD); - return fs; + PTHREAD_MUTEX_DESTROY(&fs->upcall_list_mutex, fs->pthread_flags, + GLFS_INIT_MUTEX_UPCALL); + + if (fs->oldvolfile) + FREE(fs->oldvolfile); + + FREE(fs->volname); + + FREE(fs); } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_volfile, 3.4.0) +int +pub_glfs_set_volfile(struct glfs *fs, const char *volfile) +{ + cmd_args_t *cmd_args = NULL; + + cmd_args = &fs->ctx->cmd_args; + if (vol_assigned(cmd_args)) + return -1; + + cmd_args->volfile = gf_strdup(volfile); + if (!cmd_args->volfile) + return -1; + return 0; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_logging, 3.4.0) int -glfs_set_volfile (struct glfs *fs, const char *volfile) +pub_glfs_set_logging(struct glfs *fs, const char *logfile, int loglevel) { - cmd_args_t *cmd_args = NULL; + int ret = -1; + char *tmplog = NULL; - cmd_args = &fs->ctx->cmd_args; + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); - if (vol_assigned (cmd_args)) - return -1; + if (!logfile) { + ret = gf_set_log_file_path(&fs->ctx->cmd_args, fs->ctx); + if (ret) + goto out; + tmplog = fs->ctx->cmd_args.log_file; + } else { + tmplog = (char *)logfile; + } - cmd_args->volfile = gf_strdup (volfile); + /* finish log set parameters before init */ + if (loglevel >= 0) + gf_log_set_loglevel(fs->ctx, loglevel); - return 0; -} + ret = gf_log_init(fs->ctx, tmplog, NULL); + if (ret) + goto out; + ret = gf_log_inject_timer_event(fs->ctx); + if (ret) + goto out; + +out: + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} int -glfs_set_volfile_server (struct glfs *fs, const char *transport, - const char *host, int port) +glfs_init_wait(struct glfs *fs) +{ + int ret = -1; + + /* Always a top-down call, use glfs_lock() */ + glfs_lock(fs, _gf_true); + { + while (!fs->init) + pthread_cond_wait(&fs->cond, &fs->mutex); + ret = fs->ret; + errno = fs->err; + } + glfs_unlock(fs); + + return ret; +} + +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_init_done, 3.4.0) +void +priv_glfs_init_done(struct glfs *fs, int ret) { - cmd_args_t *cmd_args = NULL; + glfs_init_cbk init_cbk; - cmd_args = &fs->ctx->cmd_args; + if (!fs) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_GLFS_FSOBJ_NULL, NULL); + goto out; + } - if (vol_assigned (cmd_args)) - return -1; + init_cbk = fs->init_cbk; - cmd_args->volfile_server = gf_strdup (host); - cmd_args->volfile_server_transport = gf_strdup (transport); - cmd_args->volfile_server_port = port; - cmd_args->max_connect_attempts = 2; + /* Always a bottom-up call, use mutex_lock() */ + pthread_mutex_lock(&fs->mutex); + { + fs->init = 1; + fs->ret = ret; + fs->err = errno; - return 0; -} + if (!init_cbk) + pthread_cond_broadcast(&fs->cond); + } + pthread_mutex_unlock(&fs->mutex); + if (init_cbk) + init_cbk(fs, ret); +out: + return; +} int -glfs_set_logging (struct glfs *fs, const char *logfile, int loglevel) +glfs_init_common(struct glfs *fs) { - int ret = 0; + int ret = -1; - if (logfile) { - ret = gf_log_init (fs->ctx, logfile); - if (ret) - return ret; - } + ret = create_master(fs); + if (ret) + return ret; - if (loglevel >= 0) - gf_log_set_loglevel (loglevel); + ret = gf_thread_create(&fs->poller, NULL, glfs_poller, fs, "glfspoll"); + if (ret) + return ret; - return ret; + ret = glfs_volumes_init(fs); + if (ret) + return ret; + + fs->dev_id = gf_dm_hashfn(fs->volname, strlen(fs->volname)); + return ret; } +int +glfs_init_async(struct glfs *fs, glfs_init_cbk cbk) +{ + int ret = -1; + + if (!fs || !fs->ctx) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_FS_NOT_INIT, NULL); + errno = EINVAL; + return ret; + } + + fs->init_cbk = cbk; + ret = glfs_init_common(fs); + + return ret; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_init, 3.4.0) int -glfs_init_wait (struct glfs *fs) +pub_glfs_init(struct glfs *fs) { - int ret = -1; + int ret = -1; + + DECLARE_OLD_THIS; + + if (!fs || !fs->ctx) { + gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_FS_NOT_INIT, NULL); + errno = EINVAL; + return ret; + } + + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); - /* Always a top-down call, use glfs_lock() */ - glfs_lock (fs); - { - while (!fs->init) - pthread_cond_wait (&fs->cond, - &fs->mutex); - ret = fs->ret; - errno = fs->err; - } - glfs_unlock (fs); + ret = glfs_init_common(fs); + if (ret) + goto out; - return ret; + ret = glfs_init_wait(fs); +out: + __GLFS_EXIT_FS; + + /* Set the initial current working directory to "/" */ + if (ret >= 0) { + ret = glfs_chdir(fs, "/"); + } + +invalid_fs: + return ret; } +static int +glusterfs_ctx_destroy(glusterfs_ctx_t *ctx) +{ + call_pool_t *pool = NULL; + int ret = 0; + glusterfs_graph_t *trav_graph = NULL; + glusterfs_graph_t *tmp = NULL; + + if (ctx == NULL) + return 0; + + if (ctx->cmd_args.curr_server) + glfs_free_volfile_servers(&ctx->cmd_args); + + glfs_free_xlator_options(&ctx->cmd_args); + + /* For all the graphs, crawl through the xlator_t structs and free + * all its members except for the mem_acct member, + * as GF_FREE will be referencing it. + */ + list_for_each_entry_safe(trav_graph, tmp, &ctx->graphs, list) + { + xlator_tree_free_members(trav_graph->first); + } + + /* Free the memory pool */ + if (ctx->stub_mem_pool) + mem_pool_destroy(ctx->stub_mem_pool); + if (ctx->dict_pool) + mem_pool_destroy(ctx->dict_pool); + if (ctx->dict_data_pool) + mem_pool_destroy(ctx->dict_data_pool); + if (ctx->dict_pair_pool) + mem_pool_destroy(ctx->dict_pair_pool); + if (ctx->logbuf_pool) + mem_pool_destroy(ctx->logbuf_pool); + + pool = ctx->pool; + if (pool) { + if (pool->frame_mem_pool) + mem_pool_destroy(pool->frame_mem_pool); + if (pool->stack_mem_pool) + mem_pool_destroy(pool->stack_mem_pool); + LOCK_DESTROY(&pool->lock); + GF_FREE(pool); + } + + /* Free the event pool */ + ret = gf_event_pool_destroy(ctx->event_pool); + + /* Free the iobuf pool */ + iobuf_pool_destroy(ctx->iobuf_pool); + + GF_FREE(ctx->process_uuid); + GF_FREE(ctx->cmd_args.volfile_id); + GF_FREE(ctx->cmd_args.process_name); + + LOCK_DESTROY(&ctx->lock); + pthread_mutex_destroy(&ctx->notify_lock); + pthread_cond_destroy(&ctx->notify_cond); + + /* Free all the graph structs and its containing xlator_t structs + * from this point there should be no reference to GF_FREE/GF_CALLOC + * as it will try to access mem_acct and the below function would + * have freed the same. + */ + list_for_each_entry_safe(trav_graph, tmp, &ctx->graphs, list) + { + glusterfs_graph_destroy_residual(trav_graph); + } + + GF_FREE(ctx->statedump_path); + FREE(ctx); + + return ret; +} -void -glfs_init_done (struct glfs *fs, int ret) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fini, 3.4.0) +int +pub_glfs_fini(struct glfs *fs) { - glfs_init_cbk init_cbk; + int ret = -1; + int countdown = 100; + xlator_t *subvol = NULL; + glusterfs_ctx_t *ctx = NULL; + glusterfs_graph_t *graph = NULL; + call_pool_t *call_pool = NULL; + int fs_init = 0; + int err = -1; + struct synctask *waittask = NULL; + + DECLARE_OLD_THIS; + + if (!fs) { + errno = EINVAL; + goto invalid_fs; + } + + ctx = fs->ctx; + if (!ctx) { + goto free_fs; + } + + THIS = fs->ctx->master; + + if (ctx->mgmt) { + rpc_clnt_disable(ctx->mgmt); + } + + call_pool = fs->ctx->pool; + + /* Wake up any suspended synctasks */ + while (!list_empty(&fs->waitq)) { + waittask = list_entry(fs->waitq.next, struct synctask, waitq); + list_del_init(&waittask->waitq); + synctask_wake(waittask); + } + + while (countdown--) { + /* give some time for background frames to finish */ + pthread_mutex_lock(&fs->mutex); + { + /* Do we need to increase countdown? */ + if ((!call_pool->cnt) && (!fs->pin_refcnt)) { + gf_msg_trace("glfs", 0, + "call_pool_cnt - %" PRId64 + "," + "pin_refcnt - %d", + call_pool->cnt, fs->pin_refcnt); + + ctx->cleanup_started = 1; + pthread_mutex_unlock(&fs->mutex); + break; + } + } + pthread_mutex_unlock(&fs->mutex); + gf_nanosleep(100000 * GF_US_IN_NS); + } + + /* leaked frames may exist, we ignore */ + + /*We deem glfs_fini as successful if there are no pending frames in the call + *pool*/ + ret = (call_pool->cnt == 0) ? 0 : -1; + + pthread_mutex_lock(&fs->mutex); + { + fs_init = fs->init; + } + pthread_mutex_unlock(&fs->mutex); + + if (fs_init != 0) { + subvol = glfs_active_subvol(fs); + if (subvol) { + /* PARENT_DOWN within glfs_subvol_done() is issued + only on graph switch (new graph should activiate + and decrement the extra @winds count taken in + glfs_graph_setup() + + Since we are explicitly destroying, + PARENT_DOWN is necessary + */ + xlator_notify(subvol, GF_EVENT_PARENT_DOWN, subvol, 0); + /* Here we wait for GF_EVENT_CHILD_DOWN before exiting, + in case of asynchrnous cleanup + */ + graph = subvol->graph; + err = pthread_mutex_lock(&fs->mutex); + if (err != 0) { + gf_smsg("glfs", GF_LOG_ERROR, err, API_MSG_FSMUTEX_LOCK_FAILED, + "error=%s", strerror(err), NULL); + goto fail; + } + /* check and wait for CHILD_DOWN for active subvol*/ + { + while (graph->used) { + err = pthread_cond_wait(&fs->child_down_cond, &fs->mutex); + if (err != 0) + gf_smsg("glfs", GF_LOG_INFO, err, + API_MSG_COND_WAIT_FAILED, "name=%s", + subvol->name, "err=%s", strerror(err), NULL); + } + } + + err = pthread_mutex_unlock(&fs->mutex); + if (err != 0) { + gf_smsg("glfs", GF_LOG_ERROR, err, + API_MSG_FSMUTEX_UNLOCK_FAILED, "error=%s", + strerror(err), NULL); + goto fail; + } + } + glfs_subvol_done(fs, subvol); + } + + ctx->cleanup_started = 1; + + if (fs_init != 0) { + /* Destroy all the inode tables of all the graphs. + * NOTE: + * - inode objects should be destroyed before calling fini() + * of each xlator, as fini() and forget() of the xlators + * can share few common locks or data structures, calling + * fini first might destroy those required by forget + * ( eg: in quick-read) + * - The call to inode_table_destroy_all is not required when + * the cleanup during graph switch is implemented to perform + * inode table destroy. + */ + inode_table_destroy_all(ctx); + + /* Call fini() of all the xlators in the active graph + * NOTE: + * - xlator fini() should be called before destroying any of + * the threads. (eg: fini() in protocol-client uses timer + * thread) */ + glusterfs_graph_deactivate(ctx->active); + + /* Join the syncenv_processor threads and cleanup + * syncenv resources*/ + syncenv_destroy(ctx->env); + + /* Join the poller thread */ + if (gf_event_dispatch_destroy(ctx->event_pool) < 0) + ret = -1; + } + + /* Avoid dispatching events to mgmt after freed, + * unreference mgmt after the event_dispatch_destroy */ + if (ctx->mgmt) { + rpc_clnt_unref(ctx->mgmt); + ctx->mgmt = NULL; + } + + /* log infra has to be brought down before destroying + * timer registry, as logging uses timer infra + */ + if (gf_log_fini(ctx) != 0) + ret = -1; + + /* Join the timer thread */ + if (fs_init != 0) { + gf_timer_registry_destroy(ctx); + } + + /* Destroy the context and the global pools */ + if (glusterfs_ctx_destroy(ctx) != 0) + ret = -1; + +free_fs: + glfs_free_from_ctx(fs); + + /* + * Do this as late as possible in case anything else has (or + * grows) a dependency on mem-pool allocations. + */ + mem_pools_fini(); + +fail: + if (!ret) + ret = err; + + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} - if (!fs) { - gf_log ("glfs", GF_LOG_ERROR, - "fs is NULL"); - goto out; - } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_get_volfile, 3.6.0) +ssize_t +pub_glfs_get_volfile(struct glfs *fs, void *buf, size_t len) +{ + ssize_t res = -1; + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + glfs_lock(fs, _gf_true); + if (len >= fs->oldvollen) { + gf_msg_trace("glfs", 0, "copying %zu to %p", len, buf); + memcpy(buf, fs->oldvolfile, len); + res = len; + } else { + res = len - fs->oldvollen; + gf_msg_trace("glfs", 0, "buffer is %zd too short", -res); + } + glfs_unlock(fs); + + __GLFS_EXIT_FS; + +invalid_fs: + return res; +} - init_cbk = fs->init_cbk; +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_ipc, 3.12.0) +int +priv_glfs_ipc(struct glfs *fs, int opcode, void *xd_in, void **xd_out) +{ + xlator_t *subvol = NULL; + int ret = -1; - /* Always a bottom-up call, use mutex_lock() */ - pthread_mutex_lock (&fs->mutex); - { - fs->init = 1; - fs->ret = ret; - fs->err = errno; + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); - if (!init_cbk) - pthread_cond_broadcast (&fs->cond); - } - pthread_mutex_unlock (&fs->mutex); + subvol = glfs_active_subvol(fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + ret = syncop_ipc(subvol, opcode, (dict_t *)xd_in, (dict_t **)xd_out); + DECODE_SYNCOP_ERR(ret); - if (init_cbk) - init_cbk (fs, ret); out: - return; -} + glfs_subvol_done(fs, subvol); + __GLFS_EXIT_FS; +invalid_fs: + return ret; +} +GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_setfspid, 6.1) int -glfs_init_common (struct glfs *fs) +priv_glfs_setfspid(struct glfs *fs, pid_t pid) { - int ret = -1; + cmd_args_t *cmd_args = NULL; + int ret = 0; - ret = create_master (fs); - if (ret) - return ret; + cmd_args = &fs->ctx->cmd_args; + cmd_args->client_pid = pid; + cmd_args->client_pid_set = 1; + ret = syncopctx_setfspid(&pid); - ret = pthread_create (&fs->poller, NULL, glfs_poller, fs); - if (ret) - return ret; + return ret; +} - ret = glfs_volumes_init (fs); - if (ret) - return ret; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_free, 3.7.16) +void +pub_glfs_free(void *ptr) +{ + GLFS_FREE(ptr); +} - fs->dev_id = gf_dm_hashfn (fs->volname, strlen (fs->volname)); - return ret; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_fs, 3.7.16) +struct glfs * +pub_glfs_upcall_get_fs(struct glfs_upcall *arg) +{ + return arg->fs; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_reason, 3.7.16) +enum glfs_upcall_reason +pub_glfs_upcall_get_reason(struct glfs_upcall *arg) +{ + return arg->reason; +} -int -glfs_init_async (struct glfs *fs, glfs_init_cbk cbk) +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_event, 3.7.16) +void * +pub_glfs_upcall_get_event(struct glfs_upcall *arg) +{ + return arg->event; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_object, 3.7.16) +struct glfs_object * +pub_glfs_upcall_inode_get_object(struct glfs_upcall_inode *arg) { - int ret = -1; + return arg->object; +} - fs->init_cbk = cbk; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_flags, 3.7.16) +uint64_t +pub_glfs_upcall_inode_get_flags(struct glfs_upcall_inode *arg) +{ + return arg->flags; +} - ret = glfs_init_common (fs); +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_stat, 3.7.16) +struct stat * +pub_glfs_upcall_inode_get_stat(struct glfs_upcall_inode *arg) +{ + return &arg->buf; +} - return ret; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_expire, 3.7.16) +uint64_t +pub_glfs_upcall_inode_get_expire(struct glfs_upcall_inode *arg) +{ + return arg->expire_time_attr; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_pobject, 3.7.16) +struct glfs_object * +pub_glfs_upcall_inode_get_pobject(struct glfs_upcall_inode *arg) +{ + return arg->p_object; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_pstat, 3.7.16) +struct stat * +pub_glfs_upcall_inode_get_pstat(struct glfs_upcall_inode *arg) +{ + return &arg->p_buf; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_oldpobject, 3.7.16) +struct glfs_object * +pub_glfs_upcall_inode_get_oldpobject(struct glfs_upcall_inode *arg) +{ + return arg->oldp_object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_oldpstat, 3.7.16) +struct stat * +pub_glfs_upcall_inode_get_oldpstat(struct glfs_upcall_inode *arg) +{ + return &arg->oldp_buf; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_lease_get_object, 4.1.6) +struct glfs_object * +pub_glfs_upcall_lease_get_object(struct glfs_upcall_lease *arg) +{ + return arg->object; +} + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_lease_get_lease_type, 4.1.6) +uint32_t +pub_glfs_upcall_lease_get_lease_type(struct glfs_upcall_lease *arg) +{ + return arg->lease_type; +} + +/* definitions of the GLFS_SYSRQ_* chars are in glfs.h */ +static struct glfs_sysrq_help { + char sysrq; + char *msg; +} glfs_sysrq_help[] = {{GLFS_SYSRQ_HELP, "(H)elp"}, + {GLFS_SYSRQ_STATEDUMP, "(S)tatedump"}, + {0, NULL}}; + +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_sysrq, 3.10.0) int -glfs_init (struct glfs *fs) +pub_glfs_sysrq(struct glfs *fs, char sysrq) { - int ret = -1; + glusterfs_ctx_t *ctx = NULL; + int ret = 0; + int msg_len; + char msg[1024] = { + 0, + }; /* should not exceed 1024 chars */ + + if (!fs || !fs->ctx) { + ret = -1; + errno = EINVAL; + goto out; + } + + ctx = fs->ctx; + + switch (sysrq) { + case GLFS_SYSRQ_HELP: { + struct glfs_sysrq_help *usage = NULL; + + for (usage = glfs_sysrq_help; usage->sysrq; usage++) { + msg_len = strlen(msg); + snprintf(msg + msg_len, /* append to msg */ + sizeof(msg) - msg_len - 2, + /* - 2 for the " " + terminating \0 */ + " %s", usage->msg); + } + + /* not really an 'error', but make sure it gets logged */ + gf_log("glfs", GF_LOG_ERROR, "available events: %s", msg); + + break; + } + case GLFS_SYSRQ_STATEDUMP: + gf_proc_dump_info(SIGUSR1, ctx); + break; + default: + gf_smsg("glfs", GF_LOG_ERROR, ENOTSUP, API_MSG_INVALID_SYSRQ, + "sysrq=%c", sysrq, NULL); + errno = ENOTSUP; + ret = -1; + } +out: + return ret; +} - ret = glfs_init_common (fs); - if (ret) - return ret; +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_register, 3.13.0) +int +pub_glfs_upcall_register(struct glfs *fs, uint32_t event_list, + glfs_upcall_cbk cbk, void *data) +{ + int ret = 0; + + /* list of supported upcall events */ + uint32_t up_events = (GLFS_EVENT_INODE_INVALIDATE | + GLFS_EVENT_RECALL_LEASE); + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + GF_VALIDATE_OR_GOTO(THIS->name, cbk, out); + + /* Event list should be either GLFS_EVENT_ANY + * or list of supported individual events (up_events) + */ + if ((event_list != GLFS_EVENT_ANY) && (event_list & ~up_events)) { + errno = EINVAL; + ret = -1; + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, + "event_list=(0x%08x)", event_list, NULL); + goto out; + } + + /* in case other thread does unregister */ + pthread_mutex_lock(&fs->mutex); + { + if (event_list & GLFS_EVENT_INODE_INVALIDATE) { + /* @todo: Check if features.cache-invalidation is + * enabled. + */ + fs->upcall_events |= GF_UPCALL_CACHE_INVALIDATION; + ret |= GLFS_EVENT_INODE_INVALIDATE; + } + if (event_list & GLFS_EVENT_RECALL_LEASE) { + /* @todo: Check if features.leases is enabled */ + fs->upcall_events |= GF_UPCALL_RECALL_LEASE; + ret |= GLFS_EVENT_RECALL_LEASE; + } + /* Override cbk function if existing */ + fs->up_cbk = cbk; + fs->up_data = data; + fs->cache_upcalls = _gf_true; + } + pthread_mutex_unlock(&fs->mutex); - ret = glfs_init_wait (fs); +out: + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return ret; } +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_unregister, 3.13.0) +int +pub_glfs_upcall_unregister(struct glfs *fs, uint32_t event_list) +{ + int ret = 0; + /* list of supported upcall events */ + uint32_t up_events = (GLFS_EVENT_INODE_INVALIDATE | + GLFS_EVENT_RECALL_LEASE); + + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + /* Event list should be either GLFS_EVENT_ANY + * or list of supported individual events (up_events) + */ + if ((event_list != GLFS_EVENT_ANY) && (event_list & ~up_events)) { + errno = EINVAL; + ret = -1; + gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, + "event_list=(0x%08x)", event_list, NULL); + goto out; + } + + pthread_mutex_lock(&fs->mutex); + { + /* We already checked if event_list contains list of supported + * upcall events. No other specific checks needed as of now for + * unregister */ + fs->upcall_events &= ~(event_list); + ret |= ((event_list == GLFS_EVENT_ANY) ? up_events : event_list); + + /* If there are no upcall events registered, reset cbk */ + if (fs->upcall_events == 0) { + fs->up_cbk = NULL; + fs->up_data = NULL; + fs->cache_upcalls = _gf_false; + } + } + pthread_mutex_unlock(&fs->mutex); + +out: + __GLFS_EXIT_FS; + +invalid_fs: + return ret; +} +GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_statedump_path, 7.0) int -glfs_fini (struct glfs *fs) +pub_glfs_set_statedump_path(struct glfs *fs, const char *path) { - int ret = -1; + struct stat st; + int ret; + DECLARE_OLD_THIS; + __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); + + if (!path) { + gf_log("glfs", GF_LOG_ERROR, "path is NULL"); + errno = EINVAL; + goto err; + } + + /* If path is not present OR, if it is directory AND has enough permission + * to create files, then proceed */ + ret = sys_stat(path, &st); + if (ret && errno != ENOENT) { + gf_log("glfs", GF_LOG_ERROR, "%s: not a valid path (%s)", path, + strerror(errno)); + errno = EINVAL; + goto err; + } + + if (!ret) { + /* file is present, now check other things */ + if (!S_ISDIR(st.st_mode)) { + gf_log("glfs", GF_LOG_ERROR, "%s: path is not directory", path); + errno = EINVAL; + goto err; + } + if (sys_access(path, W_OK | X_OK) < 0) { + gf_log("glfs", GF_LOG_ERROR, + "%s: path doesn't have write permission", path); + errno = EPERM; + goto err; + } + } + + /* If set, it needs to be freed, so we don't have leak */ + GF_FREE(fs->ctx->statedump_path); + + fs->ctx->statedump_path = gf_strdup(path); + if (!fs->ctx->statedump_path) { + gf_log("glfs", GF_LOG_ERROR, + "%s: failed to set statedump path, no memory", path); + errno = ENOMEM; + goto err; + } + + __GLFS_EXIT_FS; + + return 0; +err: + __GLFS_EXIT_FS; - return ret; +invalid_fs: + return -1; } diff --git a/api/src/glfs.h b/api/src/glfs.h index f472ca4ea1e..279d11d58ee 100644 --- a/api/src/glfs.h +++ b/api/src/glfs.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> + Copyright (c) 2012-2018 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -8,7 +8,6 @@ cases as published by the Free Software Foundation. */ - #ifndef _GLFS_H #define _GLFS_H @@ -21,6 +20,17 @@ both the library and the application. */ +/* Values for valid flags to be used when using XXXsetattr, to set multiple + attribute values passed via the related stat structure. + */ + +#define GFAPI_SET_ATTR_MODE 0x1 +#define GFAPI_SET_ATTR_UID 0x2 +#define GFAPI_SET_ATTR_GID 0x4 +#define GFAPI_SET_ATTR_SIZE 0x8 +#define GFAPI_SET_ATTR_ATIME 0x10 +#define GFAPI_SET_ATTR_MTIME 0x20 + #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif @@ -41,6 +51,65 @@ #include <sys/cdefs.h> #include <dirent.h> #include <sys/statvfs.h> +#include <stdint.h> +#include <sys/time.h> + +/* + * For off64_t to be defined, we need both + * __USE_LARGEFILE64 to be true and __off64_t_defnined to be + * false. But, making __USE_LARGEFILE64 true causes other issues + * such as redinition of stat and fstat to stat64 and fstat64 + * respectively which again causes compilation issues. + * Without off64_t being defined, this will not compile as + * copy_file_range uses off64_t. Hence define it here. First + * check whether __off64_t_defined is true or not. <unistd.h> + * sets that flag when it defines off64_t. If __off64_t_defined + * is false and __USE_FILE_OFFSET64 is true, then go on to define + * off64_t using __off64_t. + */ +#ifndef GF_BSD_HOST_OS +#if defined(__USE_FILE_OFFSET64) && !defined(__off64_t_defined) +typedef __off64_t off64_t; +#endif /* defined(__USE_FILE_OFFSET64) && !defined(__off64_t_defined) */ +#else +#include <stdio.h> +#ifndef _OFF64_T_DECLARED +/* + * Including <stdio.h> (done above) should actually define + * _OFF64_T_DECLARED with off64_t data type being available + * for consumption. But, off64_t data type is not recognizable + * for FreeBSD versions less than 11. Hence, int64_t is typedefed + * to off64_t. + */ +#define _OFF64_T_DECLARED +typedef int64_t off64_t; +#endif /* _OFF64_T_DECLARED */ +#endif /* GF_BSD_HOST_OS */ + +#if defined(HAVE_SYS_ACL_H) || (defined(USE_POSIX_ACLS) && USE_POSIX_ACLS) +#include <sys/acl.h> +#else +typedef void *acl_t; +typedef int acl_type_t; +#endif + +/* Portability non glibc c++ build systems */ +#ifndef __THROW +#if defined __cplusplus +#define __THROW throw() +#else +#define __THROW +#endif +#endif + +#ifndef GF_DARWIN_HOST_OS +#define GFAPI_PUBLIC(sym, ver) /**/ +#define GFAPI_PRIVATE(sym, ver) /**/ +#else +#define GFAPI_PUBLIC(sym, ver) __asm("_" __STRING(sym) "$GFAPI_" __STRING(ver)) +#define GFAPI_PRIVATE(sym, ver) \ + __asm("_" __STRING(sym) "$GFAPI_PRIVATE_" __STRING(ver)) +#endif __BEGIN_DECLS @@ -48,7 +117,6 @@ __BEGIN_DECLS struct glfs; typedef struct glfs glfs_t; - /* SYNOPSIS @@ -69,8 +137,8 @@ typedef struct glfs glfs_t; @volname: Name of the volume. This identifies the server-side volume and the fetched volfile (equivalent of --volfile-id command line - parameter to glusterfsd). When used with glfs_set_volfile() the - @volname has no effect (except for appearing in log messages). + parameter to glusterfsd). When used with glfs_set_volfile() the + @volname has no effect (except for appearing in log messages). RETURN VALUES @@ -79,8 +147,8 @@ typedef struct glfs glfs_t; */ -glfs_t *glfs_new (const char *volname); - +glfs_t * +glfs_new(const char *volname) __THROW GFAPI_PUBLIC(glfs_new, 3.4.0); /* SYNOPSIS @@ -109,18 +177,19 @@ glfs_t *glfs_new (const char *volname); */ -int glfs_set_volfile (glfs_t *fs, const char *volfile); - +int +glfs_set_volfile(glfs_t *fs, const char *volfile) __THROW + GFAPI_PUBLIC(glfs_set_volfile, 3.4.0); /* SYNOPSIS - glfs_set_volfile_server: Specify the address of management server. + glfs_set_volfile_server: Specify the list of addresses for management server. DESCRIPTION - This function specifies the address of the management server (glusterd) - to connect, and establish the volume configuration. The @volname + This function specifies the list of addresses for the management server + (glusterd) to connect, and establish the volume configuration. The @volname parameter passed to glfs_new() is the volume which will be virtually mounted as the glfs_t object. All operations performed by the CLI at the management server will automatically be reflected in the 'virtual @@ -135,20 +204,24 @@ int glfs_set_volfile (glfs_t *fs, const char *volfile); specification file. @transport: String specifying the transport used to connect to the - management daemon. Specifying NULL will result in the usage - of the default (tcp) transport type. Permitted values - are those what you specify as transport-type in a volume - specification file (e.g "tcp", "rdma", "unix".) + management daemon. Specifying NULL will result in the + usage of the default (tcp) transport type. Permitted + values are "tcp" or "unix". + + @host: String specifying the address where to find the management daemon. + Socket path, while using Unix domain socket as transport type. + This would either be + - FQDN (e.g : "storage01.company.com") or + - ASCII (e.g : "192.168.22.1") or + - Socket path (e.g : "/var/run/glusterd.socket") - @host: String specifying the address of where to find the management - daemon. Depending on the transport type this would either be - an FQDN (e.g: "storage01.company.com"), ASCII encoded IP - address "192.168.22.1", or a UNIX domain socket path (e.g - "/tmp/glusterd.socket".) + NOTE: This API is special, multiple calls to this function with different + volfile servers, port or transport-type would create a list of volfile + servers which would be polled during `volfile_fetch_attempts()` @port: The TCP port number where gluster management daemon is listening. Specifying 0 uses the default port number GF_DEFAULT_BASE_PORT. - This parameter is unused if you are using a UNIX domain socket. + This parameter is unused if you are using a UNIX domain socket. RETURN VALUES @@ -157,10 +230,15 @@ int glfs_set_volfile (glfs_t *fs, const char *volfile); */ -int glfs_set_volfile_server (glfs_t *fs, const char *transport, - const char *host, int port); - +int +glfs_set_volfile_server(glfs_t *fs, const char *transport, const char *host, + int port) __THROW + GFAPI_PUBLIC(glfs_set_volfile_server, 3.4.0); +int +glfs_unset_volfile_server(glfs_t *fs, const char *transport, const char *host, + int port) __THROW + GFAPI_PUBLIC(glfs_unset_volfile_server, 3.5.1); /* SYNOPSIS @@ -176,7 +254,9 @@ int glfs_set_volfile_server (glfs_t *fs, const char *transport, @fs: The 'virtual mount' object to be configured with the logging parameters. @logfile: The logfile to be used for logging. Will be created if it does not - already exist (provided system permissions allow.) + already exist (provided system permissions allow). If NULL, a new + logfile will be created in default log directory associated with + the glusterfs installation. @loglevel: Numerical value specifying the degree of verbosity. Higher the value, more verbose the logging. @@ -188,8 +268,9 @@ int glfs_set_volfile_server (glfs_t *fs, const char *transport, */ -int glfs_set_logging (glfs_t *fs, const char *logfile, int loglevel); - +int +glfs_set_logging(glfs_t *fs, const char *logfile, int loglevel) __THROW + GFAPI_PUBLIC(glfs_set_logging, 3.4.0); /* SYNOPSIS @@ -215,10 +296,105 @@ int glfs_set_logging (glfs_t *fs, const char *logfile, int loglevel); */ -int glfs_init (glfs_t *fs); +int +glfs_init(glfs_t *fs) __THROW GFAPI_PUBLIC(glfs_init, 3.4.0); + +/* + SYNOPSIS + + glfs_fini: Cleanup and destroy the 'virtual mount' + + DESCRIPTION + + This function attempts to gracefully destroy glfs_t object. An attempt is + made to wait for all background processing to complete before returning. + glfs_fini() must be called after all operations on glfs_t is finished. -int glfs_fini (glfs_t *fs); + IMPORTANT + + IT IS NECESSARY TO CALL glfs_fini() ON ALL THE INITIALIZED glfs_t + OBJECTS BEFORE TERMINATING THE PROGRAM. THERE MAY BE CACHED AND + UNWRITTEN / INCOMPLETE OPERATIONS STILL IN PROGRESS EVEN THOUGH THE + API CALLS HAVE RETURNED. glfs_fini() WILL WAIT FOR BACKGROUND OPERATIONS + TO COMPLETE BEFORE RETURNING, THEREBY MAKING IT SAFE FOR THE PROGRAM TO + EXIT. + + PARAMETERS + + @fs: The 'virtual mount' object to be destroyed. + + RETURN VALUES + + 0 : Success. +*/ + +int +glfs_fini(glfs_t *fs) __THROW GFAPI_PUBLIC(glfs_fini, 3.4.0); + +/* + SYNOPSIS + + glfs_getvol: Get the volfile associated with a 'virtual mount' + + DESCRIPTION + + Sometimes it's useful e.g. for scripts to see the volfile, so that they + can parse it and find subvolumes to do things like split-brain resolution + or custom layouts. The API here was specifically intended to make access + e.g. from Python as simple as possible. + + Note that the volume must be started (not necessarily mounted) for this + to work. + + PARAMETERS + + @fs: The 'virtual mount' object for which a volfile is desired + @buf: Pointer to a place for the volfile length to be stored + @len: Length of @buf + + RETURN VALUES + + >0: filled N bytes of buffer + 0: no volfile available + <0: volfile length exceeds @len by N bytes (@buf unchanged) +*/ + +ssize_t +glfs_get_volfile(glfs_t *fs, void *buf, size_t len) __THROW + GFAPI_PUBLIC(glfs_get_volfile, 3.6.0); + +/* + SYNOPSIS + + glfs_get_volumeid: Copy the Volume UUID stored in the glfs object fs. + + DESCRIPTION + + This function when invoked for the first time sends RPC call to the + the management server (glusterd) to fetch volume uuid and stores it + in the glusterfs_context linked to the glfs object fs which can be used + in the subsequent calls. Later it parses that UUID to convert it from + canonical string format into an opaque byte array and copy it into + the volid array. In case if either of the input parameters, volid or + size, is NULL, number of bytes required to copy the volume UUID is returned. + + PARAMETERS + + @fs: The 'virtual mount' object to be used to retrieve and store + volume's UUID. + @volid: Pointer to a place for the volume UUID to be stored + @size: Length of @volid + + RETURN VALUES + + -1 : Failure. @errno will be set with the type of failure. + Others : length of the volume UUID stored. +*/ + +int +glfs_get_volumeid(glfs_t *fs, char *volid, size_t size) __THROW + GFAPI_PUBLIC(glfs_get_volumeid, 3.5.0); /* * FILE OPERATION @@ -239,6 +415,157 @@ int glfs_fini (glfs_t *fs); struct glfs_fd; typedef struct glfs_fd glfs_fd_t; +/* + * Mask for request/result items in the struct glfs_stat. + * + * Query request/result mask for glfs_stat() (family of functions) and + * struct glfs_stat::glfs_st_mask. + * + * These bits should be set in the mask argument of glfs_stat() (family of + * functions) to request particular items when calling glfs_stat(). + * + * NOTE: Lower order 32 bits are used to reflect statx(2) bits. For Gluster + * specific attrs/extensions, use higher order 32 bits. + * + */ +#define GLFS_STAT_TYPE 0x0000000000000001U /* Want/got stx_mode & S_IFMT */ +#define GLFS_STAT_MODE 0x0000000000000002U /* Want/got stx_mode & ~S_IFMT */ +#define GLFS_STAT_NLINK 0x0000000000000004U /* Want/got stx_nlink */ +#define GLFS_STAT_UID 0x0000000000000008U /* Want/got stx_uid */ +#define GLFS_STAT_GID 0x0000000000000010U /* Want/got stx_gid */ +#define GLFS_STAT_ATIME 0x0000000000000020U /* Want/got stx_atime */ +#define GLFS_STAT_MTIME 0x0000000000000040U /* Want/got stx_mtime */ +#define GLFS_STAT_CTIME 0x0000000000000080U /* Want/got stx_ctime */ +#define GLFS_STAT_INO 0x0000000000000100U /* Want/got stx_ino */ +#define GLFS_STAT_SIZE 0x0000000000000200U /* Want/got stx_size */ +#define GLFS_STAT_BLOCKS 0x0000000000000400U /* Want/got stx_blocks */ +#define GLFS_STAT_BASIC_STATS \ + 0x00000000000007ffU /* Items in the normal stat struct */ +#define GLFS_STAT_BTIME 0x0000000000000800U /* Want/got stx_btime */ +#define GLFS_STAT_ALL 0x0000000000000fffU /* All currently supported flags */ +#define GLFS_STAT_RESERVED \ + 0x8000000000000000U /* Reserved to denote future expansion */ + +/* Macros for checking validity of struct glfs_stat members.*/ +#define GLFS_STAT_TYPE_VALID(stmask) (stmask & GLFS_STAT_TYPE) +#define GLFS_STAT_MODE_VALID(stmask) (stmask & GLFS_STAT_MODE) +#define GLFS_STAT_NLINK_VALID(stmask) (stmask & GLFS_STAT_NLINK) +#define GLFS_STAT_UID_VALID(stmask) (stmask & GLFS_STAT_UID) +#define GLFS_STAT_GID_VALID(stmask) (stmask & GLFS_STAT_GID) +#define GLFS_STAT_ATIME_VALID(stmask) (stmask & GLFS_STAT_ATIME) +#define GLFS_STAT_MTIME_VALID(stmask) (stmask & GLFS_STAT_MTIME) +#define GLFS_STAT_CTIME_VALID(stmask) (stmask & GLFS_STAT_CTIME) +#define GLFS_STAT_INO_VALID(stmask) (stmask & GLFS_STAT_INO) +#define GLFS_STAT_SIZE_VALID(stmask) (stmask & GLFS_STAT_SIZE) +#define GLFS_STAT_BLOCKS_VALID(stmask) (stmask & GLFS_STAT_BLOCKS) +#define GLFS_STAT_BTIME_VALID(stmask) (stmask & GLFS_STAT_BTIME) +#define GLFS_STAT_GFID_VALID(stmask) (stmask & GLFS_STAT_GFID) + +/* + * Attributes to be found in glfs_st_attributes and masked in + * glfs_st_attributes_mask. + * + * These give information about the features or the state of a file that might + * be of use to programs. + * + * NOTE: Lower order 32 bits are used to reflect statx(2) attribute bits. For + * Gluster specific attrs, use higher order 32 bits. + * + * NOTE: We do not support any file attributes or state as yet! + */ +#define GLFS_STAT_ATTR_RESERVED \ + 0x8000000000000000U /* Reserved to denote future expansion */ + +/* Extended file attribute structure. + * + * The caller passes a mask of what they're specifically interested in as a + * parameter to glfs_stat(). What glfs_stat() actually got will be indicated + * in glfs_st_mask upon return. + * + * For each bit in the mask argument: + * + * - if the datum is not supported: + * + * - the bit will be cleared, and + * + * - the datum value is undefined + * + * - otherwise, if explicitly requested: + * + * - the field will be filled in and the bit will be set; + * + * - otherwise, if not requested, but available in, it will be filled in + * anyway, and the bit will be set upon return; + * + * - otherwise the field and the bit will be cleared before returning. + * + */ + +struct glfs_stat { + uint64_t glfs_st_mask; /* What results were written [uncond] */ + uint64_t glfs_st_attributes; /* Flags conveying information about the file + [uncond] */ + uint64_t glfs_st_attributes_mask; /* Mask to show what's supported in + st_attributes [ucond] */ + struct timespec glfs_st_atime; /* Last access time */ + struct timespec glfs_st_btime; /* File creation time */ + struct timespec glfs_st_ctime; /* Last attribute change time */ + struct timespec glfs_st_mtime; /* Last data modification time */ + ino_t glfs_st_ino; /* Inode number */ + off_t glfs_st_size; /* File size */ + blkcnt_t glfs_st_blocks; /* Number of 512-byte blocks allocated */ + uint32_t glfs_st_rdev_major; /* Device ID of special file [if bdev/cdev] */ + uint32_t glfs_st_rdev_minor; + uint32_t glfs_st_dev_major; /* ID of device containing file [uncond] */ + uint32_t glfs_st_dev_minor; + blksize_t glfs_st_blksize; /* Preferred general I/O size [uncond] */ + nlink_t glfs_st_nlink; /* Number of hard links */ + uid_t glfs_st_uid; /* User ID of owner */ + gid_t glfs_st_gid; /* Group ID of owner */ + mode_t glfs_st_mode; /* File mode */ +}; + +#define GLFS_LEASE_ID_SIZE 16 /* 128bits */ +typedef char glfs_leaseid_t[GLFS_LEASE_ID_SIZE]; + +/* + * PER THREAD IDENTITY MODIFIERS + * + * The following operations enable to set a per thread identity context + * for the glfs APIs to perform operations as. The calls here are kept as close + * to POSIX equivalents as possible. + * + * NOTES: + * + * - setgroups is a per thread setting, hence this is named as fsgroups to be + * close in naming to the fs(u/g)id APIs + * - Typical mode of operation is to set the IDs as required, with the + * supplementary groups being optionally set, make the glfs call and post the + * glfs operation set them back to eu/gid or uid/gid as appropriate to the + * caller + * - The groups once set, need to be unset by setting the size to 0 (in which + * case the list argument is a do not care) + * - In case of leases feature enables, setfsleaseid is used to set and reset + * leaseid before and after every I/O operation. + * - Once a process for a thread of operation choses to set the IDs, all glfs + * calls made from that thread would default to the IDs set for the thread. + * As a result use these APIs with care and ensure that the set IDs are + * reverted to global process defaults as required. + * + */ +int +glfs_setfsuid(uid_t fsuid) __THROW GFAPI_PUBLIC(glfs_setfsuid, 3.4.2); + +int +glfs_setfsgid(gid_t fsgid) __THROW GFAPI_PUBLIC(glfs_setfsgid, 3.4.2); + +int +glfs_setfsgroups(size_t size, const gid_t *list) __THROW + GFAPI_PUBLIC(glfs_setfsgroups, 3.4.2); + +int +glfs_setfsleaseid(glfs_leaseid_t leaseid) __THROW + GFAPI_PUBLIC(glfs_setfsleaseid, 4.0.0); /* SYNOPSIS @@ -265,8 +592,9 @@ typedef struct glfs_fd glfs_fd_t; */ -glfs_fd_t *glfs_open (glfs_t *fs, const char *path, int flags); - +glfs_fd_t * +glfs_open(glfs_t *fs, const char *path, int flags) __THROW + GFAPI_PUBLIC(glfs_open, 3.4.0); /* SYNOPSIS @@ -294,175 +622,864 @@ glfs_fd_t *glfs_open (glfs_t *fs, const char *path, int flags); */ -glfs_fd_t *glfs_creat (glfs_t *fs, const char *path, int flags, - mode_t mode); +glfs_fd_t * +glfs_creat(glfs_t *fs, const char *path, int flags, mode_t mode) __THROW + GFAPI_PUBLIC(glfs_creat, 3.4.0); + +int +glfs_close(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_close, 3.4.0); + +glfs_t * +glfs_from_glfd(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_from_glfd, 3.4.0); + +int +glfs_set_xlator_option(glfs_t *fs, const char *xlator, const char *key, + const char *value) __THROW + GFAPI_PUBLIC(glfs_set_xlator_option, 3.4.0); + +/* -int glfs_close (glfs_fd_t *fd); + glfs_io_cbk -glfs_t *glfs_from_glfd (glfs_fd_t *fd); + The following is the function type definition of the callback + function pointer which has to be provided by the caller to the + *_async() versions of the IO calls. -int glfs_set_xlator_option (glfs_t *fs, const char *xlator, const char *key, - const char *value); + The callback function is called on completion of the requested + IO, and the appropriate return value is returned in @ret. -typedef void (*glfs_io_cbk) (glfs_fd_t *fd, ssize_t ret, void *data); + In case of an error in completing the IO, @ret will be -1 and + @errno will be set with the appropriate error. + + @ret will be same as the return value of the non _async() variant + of the particular call + + @data is the same context pointer provided by the caller at the + time of issuing the async IO call. This can be used by the + caller to differentiate different instances of the async requests + in a common callback function. + + @prestat and @poststat are allocated on the stack, that are auto destroyed + post the callback function returns. +*/ + +typedef void (*glfs_io_cbk)(glfs_fd_t *fd, ssize_t ret, + struct glfs_stat *prestat, + struct glfs_stat *poststat, void *data); // glfs_{read,write}[_async] -ssize_t glfs_read (glfs_fd_t *fd, void *buf, size_t count, int flags); -ssize_t glfs_write (glfs_fd_t *fd, const void *buf, size_t count, int flags); -int glfs_read_async (glfs_fd_t *fd, void *buf, size_t count, int flags, - glfs_io_cbk fn, void *data); -int glfs_write_async (glfs_fd_t *fd, const void *buf, size_t count, int flags, - glfs_io_cbk fn, void *data); +ssize_t +glfs_read(glfs_fd_t *fd, void *buf, size_t count, int flags) __THROW + GFAPI_PUBLIC(glfs_read, 3.4.0); + +ssize_t +glfs_write(glfs_fd_t *fd, const void *buf, size_t count, int flags) __THROW + GFAPI_PUBLIC(glfs_write, 3.4.0); + +int +glfs_read_async(glfs_fd_t *fd, void *buf, size_t count, int flags, + glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_read_async, 6.0); + +int +glfs_write_async(glfs_fd_t *fd, const void *buf, size_t count, int flags, + glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_write_async, 6.0); // glfs_{read,write}v[_async] -ssize_t glfs_readv (glfs_fd_t *fd, const struct iovec *iov, int iovcnt, - int flags); -ssize_t glfs_writev (glfs_fd_t *fd, const struct iovec *iov, int iovcnt, - int flags); -int glfs_readv_async (glfs_fd_t *fd, const struct iovec *iov, int count, - int flags, glfs_io_cbk fn, void *data); -int glfs_writev_async (glfs_fd_t *fd, const struct iovec *iov, int count, - int flags, glfs_io_cbk fn, void *data); +ssize_t +glfs_readv(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, + int flags) __THROW GFAPI_PUBLIC(glfs_readv, 3.4.0); + +ssize_t +glfs_writev(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, + int flags) __THROW GFAPI_PUBLIC(glfs_writev, 3.4.0); + +int +glfs_readv_async(glfs_fd_t *fd, const struct iovec *iov, int count, int flags, + glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_readv_async, 6.0); + +int +glfs_writev_async(glfs_fd_t *fd, const struct iovec *iov, int count, int flags, + glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_writev_async, 6.0); // glfs_p{read,write}[_async] -ssize_t glfs_pread (glfs_fd_t *fd, void *buf, size_t count, off_t offset, - int flags); -ssize_t glfs_pwrite (glfs_fd_t *fd, const void *buf, size_t count, - off_t offset, int flags); -int glfs_pread_async (glfs_fd_t *fd, void *buf, size_t count, off_t offset, - int flags, glfs_io_cbk fn, void *data); -int glfs_pwrite_async (glfs_fd_t *fd, const void *buf, int count, off_t offset, - int flags, glfs_io_cbk fn, void *data); +ssize_t +glfs_pread(glfs_fd_t *fd, void *buf, size_t count, off_t offset, int flags, + struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_pread, 6.0); + +ssize_t +glfs_pwrite(glfs_fd_t *fd, const void *buf, size_t count, off_t offset, + int flags, struct glfs_stat *prestat, + struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_pwrite, 6.0); + +int +glfs_pread_async(glfs_fd_t *fd, void *buf, size_t count, off_t offset, + int flags, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_pread_async, 6.0); + +int +glfs_pwrite_async(glfs_fd_t *fd, const void *buf, int count, off_t offset, + int flags, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_pwrite_async, 6.0); // glfs_p{read,write}v[_async] -ssize_t glfs_preadv (glfs_fd_t *fd, const struct iovec *iov, int iovcnt, - off_t offset, int flags); -ssize_t glfs_pwritev (glfs_fd_t *fd, const struct iovec *iov, int iovcnt, - off_t offset, int flags); -int glfs_preadv_async (glfs_fd_t *fd, const struct iovec *iov, int count, - off_t offset, int flags, glfs_io_cbk fn, void *data); -int glfs_pwritev_async (glfs_fd_t *fd, const struct iovec *iov, int count, - off_t offset, int flags, glfs_io_cbk fn, void *data); +ssize_t +glfs_preadv(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, off_t offset, + int flags) __THROW GFAPI_PUBLIC(glfs_preadv, 3.4.0); + +ssize_t +glfs_pwritev(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, off_t offset, + int flags) __THROW GFAPI_PUBLIC(glfs_pwritev, 3.4.0); + +int +glfs_preadv_async(glfs_fd_t *fd, const struct iovec *iov, int count, + off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_preadv_async, 6.0); + +int +glfs_pwritev_async(glfs_fd_t *fd, const struct iovec *iov, int count, + off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_pwritev_async, 6.0); + +off_t +glfs_lseek(glfs_fd_t *fd, off_t offset, int whence) __THROW + GFAPI_PUBLIC(glfs_lseek, 3.4.0); + +ssize_t +glfs_copy_file_range(struct glfs_fd *glfd_in, off64_t *off_in, + struct glfs_fd *glfd_out, off64_t *off_out, size_t len, + unsigned int flags, struct glfs_stat *statbuf, + struct glfs_stat *prestat, + struct glfs_stat *poststat) __THROW + GFAPI_PUBLIC(glfs_copy_file_range, 6.0); + +int +glfs_truncate(glfs_t *fs, const char *path, off_t length) __THROW + GFAPI_PUBLIC(glfs_truncate, 3.7.15); + +int +glfs_ftruncate(glfs_fd_t *fd, off_t length, struct glfs_stat *prestat, + struct glfs_stat *poststat) __THROW + GFAPI_PUBLIC(glfs_ftruncate, 6.0); + +int +glfs_ftruncate_async(glfs_fd_t *fd, off_t length, glfs_io_cbk fn, + void *data) __THROW + GFAPI_PUBLIC(glfs_ftruncate_async, 6.0); + +int +glfs_lstat(glfs_t *fs, const char *path, struct stat *buf) __THROW + GFAPI_PUBLIC(glfs_lstat, 3.4.0); + +int +glfs_stat(glfs_t *fs, const char *path, struct stat *buf) __THROW + GFAPI_PUBLIC(glfs_stat, 3.4.0); + +int +glfs_fstat(glfs_fd_t *fd, struct stat *buf) __THROW + GFAPI_PUBLIC(glfs_fstat, 3.4.0); + +int +glfs_fsync(glfs_fd_t *fd, struct glfs_stat *prestat, + struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_fsync, 6.0); + +int +glfs_fsync_async(glfs_fd_t *fd, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_fsync_async, 6.0); + +int +glfs_fdatasync(glfs_fd_t *fd, struct glfs_stat *prestat, + struct glfs_stat *poststat) __THROW + GFAPI_PUBLIC(glfs_fdatasync, 6.0); + +int +glfs_fdatasync_async(glfs_fd_t *fd, glfs_io_cbk fn, void *data) __THROW + GFAPI_PUBLIC(glfs_fdatasync_async, 6.0); + +int +glfs_access(glfs_t *fs, const char *path, int mode) __THROW + GFAPI_PUBLIC(glfs_access, 3.4.0); + +int +glfs_symlink(glfs_t *fs, const char *oldpath, const char *newpath) __THROW + GFAPI_PUBLIC(glfs_symlink, 3.4.0); + +int +glfs_readlink(glfs_t *fs, const char *path, char *buf, size_t bufsiz) __THROW + GFAPI_PUBLIC(glfs_readlink, 3.4.0); + +int +glfs_mknod(glfs_t *fs, const char *path, mode_t mode, dev_t dev) __THROW + GFAPI_PUBLIC(glfs_mknod, 3.4.0); + +int +glfs_mkdir(glfs_t *fs, const char *path, mode_t mode) __THROW + GFAPI_PUBLIC(glfs_mkdir, 3.4.0); + +int +glfs_unlink(glfs_t *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_unlink, 3.4.0); + +int +glfs_rmdir(glfs_t *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_rmdir, 3.4.0); + +int +glfs_rename(glfs_t *fs, const char *oldpath, const char *newpath) __THROW + GFAPI_PUBLIC(glfs_rename, 3.4.0); + +int +glfs_link(glfs_t *fs, const char *oldpath, const char *newpath) __THROW + GFAPI_PUBLIC(glfs_link, 3.4.0); + +glfs_fd_t * +glfs_opendir(glfs_t *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_opendir, 3.4.0); + +/* + * @glfs_readdir_r and @glfs_readdirplus_r ARE thread safe AND re-entrant, + * but the interface has ambiguity about the size of @dirent to be allocated + * before calling the APIs. 512 byte buffer (for @dirent) is sufficient for + * all known systems which are tested againt glusterfs/gfapi, but may be + * insufficient in the future. + */ + +int +glfs_readdir_r(glfs_fd_t *fd, struct dirent *dirent, + struct dirent **result) __THROW + GFAPI_PUBLIC(glfs_readdir_r, 3.4.0); +int +glfs_readdirplus_r(glfs_fd_t *fd, struct stat *stat, struct dirent *dirent, + struct dirent **result) __THROW + GFAPI_PUBLIC(glfs_readdirplus_r, 3.4.0); -off_t glfs_lseek (glfs_fd_t *fd, off_t offset, int whence); +/* + * @glfs_readdir and @glfs_readdirplus are NEITHER thread safe NOR re-entrant + * when called on the same directory handle. However they ARE thread safe + * AND re-entrant when called on different directory handles (which may be + * referring to the same directory too.) + */ + +struct dirent * +glfs_readdir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_readdir, 3.5.0); + +struct dirent * +glfs_readdirplus(glfs_fd_t *fd, struct stat *stat) __THROW + GFAPI_PUBLIC(glfs_readdirplus, 3.5.0); + +long +glfs_telldir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_telldir, 3.4.0); + +void +glfs_seekdir(glfs_fd_t *fd, long offset) __THROW + GFAPI_PUBLIC(glfs_seekdir, 3.4.0); + +int +glfs_closedir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_closedir, 3.4.0); + +int +glfs_statvfs(glfs_t *fs, const char *path, struct statvfs *buf) __THROW + GFAPI_PUBLIC(glfs_statvfs, 3.4.0); + +int +glfs_chmod(glfs_t *fs, const char *path, mode_t mode) __THROW + GFAPI_PUBLIC(glfs_chmod, 3.4.0); -int glfs_truncate (glfs_t *fs, const char *path, off_t length); +int +glfs_fchmod(glfs_fd_t *fd, mode_t mode) __THROW + GFAPI_PUBLIC(glfs_fchmod, 3.4.0); -int glfs_ftruncate (glfs_fd_t *fd, off_t length); -int glfs_ftruncate_async (glfs_fd_t *fd, off_t length, glfs_io_cbk fn, - void *data); +int +glfs_chown(glfs_t *fs, const char *path, uid_t uid, gid_t gid) __THROW + GFAPI_PUBLIC(glfs_chown, 3.4.0); -int glfs_lstat (glfs_t *fs, const char *path, struct stat *buf); -int glfs_stat (glfs_t *fs, const char *path, struct stat *buf); -int glfs_fstat (glfs_fd_t *fd, struct stat *buf); +int +glfs_lchown(glfs_t *fs, const char *path, uid_t uid, gid_t gid) __THROW + GFAPI_PUBLIC(glfs_lchown, 3.4.0); -int glfs_fsync (glfs_fd_t *fd); -int glfs_fsync_async (glfs_fd_t *fd, glfs_io_cbk fn, void *data); +int +glfs_fchown(glfs_fd_t *fd, uid_t uid, gid_t gid) __THROW + GFAPI_PUBLIC(glfs_fchown, 3.4.0); -int glfs_fdatasync (glfs_fd_t *fd); -int glfs_fdatasync_async (glfs_fd_t *fd, glfs_io_cbk fn, void *data); +int +glfs_utimens(glfs_t *fs, const char *path, + const struct timespec times[2]) __THROW + GFAPI_PUBLIC(glfs_utimens, 3.4.0); -int glfs_access (glfs_t *fs, const char *path, int mode); +int +glfs_lutimens(glfs_t *fs, const char *path, + const struct timespec times[2]) __THROW + GFAPI_PUBLIC(glfs_lutimens, 3.4.0); -int glfs_symlink (glfs_t *fs, const char *oldpath, const char *newpath); +int +glfs_futimens(glfs_fd_t *fd, const struct timespec times[2]) __THROW + GFAPI_PUBLIC(glfs_futimens, 3.4.0); -int glfs_readlink (glfs_t *fs, const char *path, char *buf, size_t bufsiz); +ssize_t +glfs_getxattr(glfs_t *fs, const char *path, const char *name, void *value, + size_t size) __THROW GFAPI_PUBLIC(glfs_getxattr, 3.4.0); -int glfs_mknod (glfs_t *fs, const char *path, mode_t mode, dev_t dev); +ssize_t +glfs_lgetxattr(glfs_t *fs, const char *path, const char *name, void *value, + size_t size) __THROW GFAPI_PUBLIC(glfs_lgetxattr, 3.4.0); -int glfs_mkdir (glfs_t *fs, const char *path, mode_t mode); +ssize_t +glfs_fgetxattr(glfs_fd_t *fd, const char *name, void *value, + size_t size) __THROW GFAPI_PUBLIC(glfs_fgetxattr, 3.4.0); -int glfs_unlink (glfs_t *fs, const char *path); +ssize_t +glfs_listxattr(glfs_t *fs, const char *path, void *value, size_t size) __THROW + GFAPI_PUBLIC(glfs_listxattr, 3.4.0); -int glfs_rmdir (glfs_t *fs, const char *path); +ssize_t +glfs_llistxattr(glfs_t *fs, const char *path, void *value, size_t size) __THROW + GFAPI_PUBLIC(glfs_llistxattr, 3.4.0); -int glfs_rename (glfs_t *fs, const char *oldpath, const char *newpath); +ssize_t +glfs_flistxattr(glfs_fd_t *fd, void *value, size_t size) __THROW + GFAPI_PUBLIC(glfs_flistxattr, 3.4.0); -int glfs_link (glfs_t *fs, const char *oldpath, const char *newpath); +int +glfs_setxattr(glfs_t *fs, const char *path, const char *name, const void *value, + size_t size, int flags) __THROW + GFAPI_PUBLIC(glfs_setxattr, 3.4.0); -glfs_fd_t *glfs_opendir (glfs_t *fs, const char *path); +int +glfs_lsetxattr(glfs_t *fs, const char *path, const char *name, + const void *value, size_t size, int flags) __THROW + GFAPI_PUBLIC(glfs_lsetxattr, 3.4.0); -int glfs_readdir_r (glfs_fd_t *fd, struct dirent *dirent, - struct dirent **result); +int +glfs_fsetxattr(glfs_fd_t *fd, const char *name, const void *value, size_t size, + int flags) __THROW GFAPI_PUBLIC(glfs_fsetxattr, 3.4.0); -int glfs_readdirplus_r (glfs_fd_t *fd, struct stat *stat, struct dirent *dirent, - struct dirent **result); +int +glfs_removexattr(glfs_t *fs, const char *path, const char *name) __THROW + GFAPI_PUBLIC(glfs_removexattr, 3.4.0); -long glfs_telldir (glfs_fd_t *fd); +int +glfs_lremovexattr(glfs_t *fs, const char *path, const char *name) __THROW + GFAPI_PUBLIC(glfs_lremovexattr, 3.4.0); -void glfs_seekdir (glfs_fd_t *fd, long offset); +int +glfs_fremovexattr(glfs_fd_t *fd, const char *name) __THROW + GFAPI_PUBLIC(glfs_fremovexattr, 3.4.0); -int glfs_closedir (glfs_fd_t *fd); +int +glfs_fallocate(glfs_fd_t *fd, int keep_size, off_t offset, size_t len) __THROW + GFAPI_PUBLIC(glfs_fallocate, 3.5.0); -int glfs_statvfs (glfs_t *fs, const char *path, struct statvfs *buf); +int +glfs_discard(glfs_fd_t *fd, off_t offset, size_t len) __THROW + GFAPI_PUBLIC(glfs_discard, 3.5.0); -int glfs_chmod (glfs_t *fs, const char *path, mode_t mode); +int +glfs_discard_async(glfs_fd_t *fd, off_t length, size_t lent, glfs_io_cbk fn, + void *data) __THROW GFAPI_PUBLIC(glfs_discard_async, 6.0); -int glfs_fchmod (glfs_fd_t *fd, mode_t mode); +int +glfs_zerofill(glfs_fd_t *fd, off_t offset, off_t len) __THROW + GFAPI_PUBLIC(glfs_zerofill, 3.5.0); -int glfs_chown (glfs_t *fs, const char *path, uid_t uid, gid_t gid); +int +glfs_zerofill_async(glfs_fd_t *fd, off_t length, off_t len, glfs_io_cbk fn, + void *data) __THROW GFAPI_PUBLIC(glfs_zerofill_async, 6.0); -int glfs_lchown (glfs_t *fs, const char *path, uid_t uid, gid_t gid); +char * +glfs_getcwd(glfs_t *fs, char *buf, size_t size) __THROW + GFAPI_PUBLIC(glfs_getcwd, 3.4.0); -int glfs_fchown (glfs_fd_t *fd, uid_t uid, gid_t gid); +int +glfs_chdir(glfs_t *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_chdir, 3.4.0); -int glfs_utimens (glfs_t *fs, const char *path, struct timespec times[2]); +int +glfs_fchdir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_fchdir, 3.4.0); -int glfs_lutimens (glfs_t *fs, const char *path, struct timespec times[2]); +char * +glfs_realpath(glfs_t *fs, const char *path, char *resolved_path) __THROW + GFAPI_PUBLIC(glfs_realpath, 3.7.17); -int glfs_futimens (glfs_fd_t *fd, struct timespec times[2]); +/* + * @cmd and @flock are as specified in man fcntl(2). + */ +int +glfs_posix_lock(glfs_fd_t *fd, int cmd, struct flock *flock) __THROW + GFAPI_PUBLIC(glfs_posix_lock, 3.4.0); -ssize_t glfs_getxattr (glfs_t *fs, const char *path, const char *name, - void *value, size_t size); +/* + SYNOPSIS -ssize_t glfs_lgetxattr (glfs_t *fs, const char *path, const char *name, - void *value, size_t size); + glfs_file_lock: Request extended byte range lock on a file -ssize_t glfs_fgetxattr (glfs_fd_t *fd, const char *name, - void *value, size_t size); + DESCRIPTION -ssize_t glfs_listxattr (glfs_t *fs, const char *path, void *value, size_t size); + This function is capable of requesting either advisory or mandatory type + byte range locks on a file. -ssize_t glfs_llistxattr (glfs_t *fs, const char *path, void *value, - size_t size); + Note: To set a unique owner key for locks based on a particular file + descriptor, make use of glfs_fd_set_lkowner() api to do so before + requesting lock via this api. This owner key will be further consumed + by other incoming data modifying file operations via the same file + descriptor. -ssize_t glfs_flistxattr (glfs_fd_t *fd, void *value, size_t size); + PARAMETERS -int glfs_setxattr (glfs_t *fs, const char *path, const char *name, - const void *value, size_t size, int flags); + @fd: File descriptor -int glfs_lsetxattr (glfs_t *fs, const char *path, const char *name, - const void *value, size_t size, int flags); + @cmd: As specified in man fcntl(2). -int glfs_fsetxattr (glfs_fd_t *fd, const char *name, - const void *value, size_t size, int flags); + @flock: As specified in man fcntl(2). -int glfs_removexattr (glfs_t *fs, const char *path, const char *name); + @lk_mode: Required lock type from options available with the + enum glfs_lock_mode_t defined below. -int glfs_lremovexattr (glfs_t *fs, const char *path, const char *name); + RETURN VALUES -int glfs_fremovexattr (glfs_fd_t *fd, const char *name); + 0 : Success. Lock has been granted. + -1 : Failure. @errno will be set indicating the type of failure. -char *glfs_getcwd (glfs_t *fs, char *buf, size_t size); + */ -int glfs_chdir (glfs_t *fs, const char *path); +/* Lock modes used by glfs_file_lock() */ +enum glfs_lock_mode { GLFS_LK_ADVISORY = 0, GLFS_LK_MANDATORY }; +typedef enum glfs_lock_mode glfs_lock_mode_t; -int glfs_fchdir (glfs_fd_t *fd); +int +glfs_file_lock(glfs_fd_t *fd, int cmd, struct flock *flock, + glfs_lock_mode_t lk_mode) __THROW + GFAPI_PUBLIC(glfs_file_lock, 3.13.0); -char *glfs_realpath (glfs_t *fs, const char *path, char *resolved_path); +glfs_fd_t * +glfs_dup(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_dup, 3.4.0); + +void +glfs_free(void *ptr) __THROW GFAPI_PUBLIC(glfs_free, 3.7.16); /* - * @cmd and @flock are as specified in man fcntl(2). + * glfs_sysrq: send a system request to the @fs instance + * + * Different commands for @sysrq are possible, the defines for these are listed + * below the function definition. + * + * This function always returns success if the @sysrq is recognized. The return + * value does not way anythin about the result of the @sysrq execution. Not all + * @sysrq command will be able to return a success/failure status. */ -int glfs_posix_lock (glfs_fd_t *fd, int cmd, struct flock *flock); +int +glfs_sysrq(glfs_t *fs, char sysrq) __THROW GFAPI_PUBLIC(glfs_sysrq, 3.10.0); -glfs_fd_t *glfs_dup (glfs_fd_t *fd); +#define GLFS_SYSRQ_HELP 'h' /* log a message with supported sysrq commands */ +#define GLFS_SYSRQ_STATEDUMP 's' /* create a statedump */ -__END_DECLS +/* + * Structure returned as part of xreaddirplus + */ +struct glfs_xreaddirp_stat; +typedef struct glfs_xreaddirp_stat glfs_xreaddirp_stat_t; + +/* Request flags to be used in XREADDIRP operation */ +#define GFAPI_XREADDIRP_NULL \ + 0x00000000 /* by default, no stat will be fetched */ +#define GFAPI_XREADDIRP_STAT 0x00000001 /* Get stat */ +#define GFAPI_XREADDIRP_HANDLE 0x00000002 /* Get object handle */ +/* + * This stat structure returned gets freed as part of glfs_free(xstat) + */ +struct stat * +glfs_xreaddirplus_get_stat(glfs_xreaddirp_stat_t *xstat) __THROW + GFAPI_PUBLIC(glfs_xreaddirplus_get_stat, 3.11.0); + +/* + * SYNOPSIS + * + * glfs_xreaddirplus_r: Extended Readirplus operation + * + * DESCRIPTION + * + * This API does readdirplus operation, but along with stat it can fetch other + * extra information like object handles etc for each of the dirents returned + * based on requested flags. On success it returns the set of flags successfully + * processed. + * + * Note that there are chances that some of the requested information may not be + * available or returned (for example if reached EOD). Ensure to validate the + * returned value to determine what flags have been successfully processed + * & set. + * + * PARAMETERS + * + * INPUT: + * @glfd: GFAPI file descriptor of the directory + * @flags: Flags determining xreaddirp_stat requested + * Current available values are: + * GFAPI_XREADDIRP_NULL + * GFAPI_XREADDIRP_STAT + * GFAPI_XREADDIRP_HANDLE + * @ext: Dirent struture to copy the values to + * (though optional recommended to be allocated by application + * esp., in multi-threaded environment) + * + * OUTPUT: + * @res: to store the next dirent value. If NULL and return value is '0', + * it means it reached end of the directory. + * @xstat_p: Pointer to contain all the requested data returned + * for that dirent. Application should make use of glfs_free() API + * to free this pointer and the variables returned by + * glfs_xreaddirplus_get_*() APIs. + * + * RETURN VALUE: + * >=0: SUCCESS (value contains the flags successfully processed) + * -1: FAILURE + */ +int +glfs_xreaddirplus_r(glfs_fd_t *glfd, uint32_t flags, + glfs_xreaddirp_stat_t **xstat_p, struct dirent *ext, + struct dirent **res) __THROW + GFAPI_PUBLIC(glfs_xreaddirplus_r, 3.11.0); + +#define GFAPI_MAX_LOCK_OWNER_LEN 255 + +/* + * + * DESCRIPTION + * + * This API allows application to set lk_owner on a fd. + * A glfd can be associated with only single lk_owner. In case if there + * is need to set another lk_owner, applications can make use of + * 'glfs_dup' to get duplicate glfd and set new lk_owner on that second + * glfd. + * + * Also its not recommended to override or clear lk_owner value as the + * same shall be used to flush any outstanding locks while closing the fd. + * + * PARAMETERS + * + * INPUT: + * @glfd: GFAPI file descriptor + * @len: Size of lk_owner buffer. Max value can be GFAPI_MAX_LOCK_OWNER_LEN + * @data: lk_owner data buffer. + * + * OUTPUT: + * 0: SUCCESS + * -1: FAILURE + */ +int +glfs_fd_set_lkowner(glfs_fd_t *glfd, void *data, int len) __THROW + GFAPI_PUBLIC(glfs_fd_set_lkowner, 3.10.7); + +/* + * Applications (currently NFS-Ganesha) can make use of this + * structure to read upcall notifications sent by server either + * by polling or registering a callback function. + * + * On success, applications need to check for 'reason' to decide + * if any upcall event is received. + * + * Currently supported upcall_events - + * GLFS_UPCALL_INODE_INVALIDATE - + * 'event_arg' - glfs_upcall_inode + * + * After processing the event, applications need to free 'event_arg' with + * glfs_free(). + * + * Also similar to I/Os, the application should ideally stop polling + * or unregister upcall_cbk function before calling glfs_fini(..). + * Hence making an assumption that 'fs' & ctx structures cannot be + * freed while in this routine. + */ +struct glfs_upcall; +typedef struct glfs_upcall glfs_upcall_t; + +glfs_t * +glfs_upcall_get_fs(glfs_upcall_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_get_fs, 3.7.16); + +enum glfs_upcall_reason { + GLFS_UPCALL_EVENT_NULL = 0, + GLFS_UPCALL_INODE_INVALIDATE, /* invalidate cache entry */ + GLFS_UPCALL_RECALL_LEASE, /* recall lease */ +}; +typedef enum glfs_upcall_reason glfs_upcall_reason_t; + +glfs_upcall_reason_t +glfs_upcall_get_reason(glfs_upcall_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_get_reason, 3.7.16); + +/* + * Applications first need to make use of above API i.e, + * "glfs_upcall_get_reason" to determine which upcall event it has + * received. Post that below API - "glfs_upcall_get_event" should + * be used to get corresponding upcall event object. + * + * Below are the upcall_reason and corresponding upcall_event objects: + * ========================================================== + * glfs_upcall_reason - event_object + * ========================================================== + * GLFS_UPCALL_EVENT_NULL - NULL + * GLFS_UPCALL_INODE_INVALIDATE - struct glfs_upcall_inode + * GLFS_UPCALL_RECALL_LEASE - struct glfs_upcall_lease + * + * After processing upcall event, glfs_free() should be called on the + * glfs_upcall. + */ +void * +glfs_upcall_get_event(glfs_upcall_t *arg) __THROW + GFAPI_PUBLIC(glfs_upcall_get_event, 3.7.16); + +/* + * SYNOPSIS + * + * glfs_upcall_cbk: Upcall callback definition + * + * This is function type definition of the callback function pointer + * which has to be provided by the caller while registering for any + * upcall events. + * + * This function is called whenever any upcall which the application + * has registered for is received from the server. + * + * @up_arg: Upcall structure whose contents need to be interpreted by + * making use of glfs_upcall_* helper routines. + * + * @data: The same context pointer provided by the caller at the time of + * registering of upcall events. This may be used by the caller for any + * of its internal use while processing upcalls. + */ +typedef void (*glfs_upcall_cbk)(glfs_upcall_t *up_arg, void *data); + +/* + * List of upcall events supported by gluster/gfapi + */ +#define GLFS_EVENT_INODE_INVALIDATE 0x00000001 /* invalidate cache entry */ +#define GLFS_EVENT_RECALL_LEASE 0x00000002 /* Recall lease */ +#define GLFS_EVENT_ANY 0xffffffff /* for all the above events */ + +/* + * SYNOPSIS + * + * glfs_upcall_register: Register for upcall events + * + * DESCRIPTION + * + * This function is used to register for various upcall events application + * is interested in and the callback function to be invoked when such + * events are triggered. + * + * Multiple calls of this routine shall override cbk function. That means + * only one cbk function can be used for all the upcall events registered + * and that shall be the one last updated. + * + * PARAMETERS: + * + * INPUT: + * @fs: The 'virtual mount' object + * + * @event_list: List of upcall events to be registered. + * Current available values are: + * - GLFS_EVENT_INODE_INVALIDATE + * - GLFS_EVENT_RECALL_LEASE + * + * @cbk: The cbk routine to be invoked in case of any upcall received + * @data: Any opaque pointer provided by caller which shall be using while + * making cbk calls. This pointer may be used by caller for any of its + * internal use while processing upcalls. Can be NULL. + * + * RETURN VALUE: + * >0: SUCCESS (value contains the events successfully registered) + * -1: FAILURE + */ +int +glfs_upcall_register(glfs_t *fs, uint32_t event_list, glfs_upcall_cbk cbk, + void *data) __THROW + GFAPI_PUBLIC(glfs_upcall_register, 3.13.0); + +/* + * SYNOPSIS + * + * glfs_upcall_unregister: Unregister for upcall events + * + * DESCRIPTION + * + * This function is used to unregister the upcall events application + * is not interested in. In case if the caller unregisters all the events + * it has registered for, it shall no more receive any upcall event. + * + * PARAMETERS: + * + * INPUT: + * @fs: The 'virtual mount' object + * + * @event_list: List of upcall events to be unregistered. + * Current available values are: + * - GLFS_EVENT_INODE_INVALIDATE + * - GLFS_EVENT_RECALL_LEASE + * RETURN VALUE: + * >0: SUCCESS (value contains the events successfully unregistered) + * -1: FAILURE + */ +int +glfs_upcall_unregister(glfs_t *fs, uint32_t event_list) __THROW + GFAPI_PUBLIC(glfs_upcall_unregister, 3.13.0); + +/* Lease Types */ +enum glfs_lease_types { + GLFS_LEASE_NONE = 0, + GLFS_RD_LEASE = 1, + GLFS_RW_LEASE = 2, +}; +typedef enum glfs_lease_types glfs_lease_types_t; + +/* Lease cmds */ +enum glfs_lease_cmds { + GLFS_GET_LEASE = 1, + GLFS_SET_LEASE = 2, + GLFS_UNLK_LEASE = 3, +}; +typedef enum glfs_lease_cmds glfs_lease_cmds_t; + +struct glfs_lease { + glfs_lease_cmds_t cmd; + glfs_lease_types_t lease_type; + glfs_leaseid_t lease_id; + unsigned int lease_flags; +}; +typedef struct glfs_lease glfs_lease_t; + +typedef void (*glfs_recall_cbk)(glfs_lease_t lease, void *data); + +/* + SYNOPSIS + + glfs_lease: Takes a lease on a file. + + DESCRIPTION + + This function takes lease on an open file. + + PARAMETERS + + @glfd: The fd of the file on which lease should be taken, + this fd is returned by glfs_open/glfs_create. + + @lease: Struct that defines the lease operation to be performed + on the file. + @lease.cmd - Can be one of the following values + GF_GET_LEASE: Get the lease type currently present on the file, + lease.lease_type will contain GF_RD_LEASE + or GF_RW_LEASE or 0 if no leases. + GF_SET_LEASE: Set the lease of given lease.lease_type on the file. + GF_UNLK_LEASE: Unlock the lease present on the given fd. + Note that the every lease request should have + a corresponding unlk_lease. + + @lease.lease_type - Can be one of the following values + GF_RD_LEASE: Read lease on a file, shared lease. + GF_RW_LEASE: Read-Write lease on a file, exclusive lease. + + @lease.lease_id - A unique identification of lease, 128bits. + + @fn: This is the function that is invoked when the lease has to be recalled + @data: It is a cookie, this pointer is returned as a part of recall + + fn and data field are stored as a part of glfs_fd, hence if there are multiple + glfs_lease calls, each of them updates the fn and data fields. glfs_recall_cbk + will be invoked with the last updated fn and data + + RETURN VALUES + 0: Successful completion + <0: Failure. @errno will be set with the type of failure +*/ + +int +glfs_lease(glfs_fd_t *glfd, glfs_lease_t *lease, glfs_recall_cbk fn, + void *data) __THROW GFAPI_PUBLIC(glfs_lease, 4.0.0); + +/* + SYNOPSIS + + glfs_fsetattr: Function to set attributes. + glfs_setattr: Function to set attributes + + DESCRIPTION + + The functions are used to set attributes on the file. + + PARAMETERS + + @glfs_fsetattr + + @glfd: The fd of the file for which the attributes are to be set, + this fd is returned by glfs_open/glfs_create. + + @glfs_setattr + + @fs: File object. + + @path: The path of the file that is being operated on. + + @follow: Flag used to resolve symlink. + + + @stat: Struct that has information about the file. + + @valid: This is the mask bit, that accepts GFAPI_SET_ATTR* masks. + Refer glfs.h to see the mask definitions. + + Both functions are similar in functionality, just that the + func setattr() uses file path whereas the func fsetattr() + uses the fd. + + RETURN VALUES + 0: Successful completion + <0: Failure. @errno will be set with the type of failure + + */ + +int +glfs_fsetattr(struct glfs_fd *glfd, struct glfs_stat *stat) __THROW + GFAPI_PUBLIC(glfs_fsetattr, 6.0); + +int +glfs_setattr(struct glfs *fs, const char *path, struct glfs_stat *stat, + int follow) __THROW GFAPI_PUBLIC(glfs_setattr, 6.0); + +/* + SYNOPSIS + + glfs_set_statedump_path: Function to set statedump path. + + DESCRIPTION + + This function is used to set statedump directory + + PARAMETERS + + @fs: The 'virtual mount' object to be configured with the volume + specification file. + + @path: statedump path. Should be a directory. But the API won't fail if the + directory doesn't exist yet, as one may create it later. + + RETURN VALUES + + 0 : Success. + -1 : Failure. @errno will be set with the type of failure. + + */ + +int +glfs_set_statedump_path(struct glfs *fs, const char *path) __THROW + GFAPI_PUBLIC(glfs_set_statedump_path, 7.0); + +__END_DECLS #endif /* !_GLFS_H */ |
