/** * PVFSIOStore::Readlink: read symbolic link * * @param link the link to read * @param buf the place the write the result * @param the size of the result buffer * @param readlen return size * @return PLFS_SUCCESS or PLFS_E* on error */ plfs_error_t PVFSIOStore::Readlink(const char *link, char *buf, size_t bufsize, ssize_t *readlen) { char *cpath; PVFS_object_ref ref; PVFS_credentials creds; size_t l; int nev, pev, cpy; PVFS_sysresp_getattr resp; cpath = pvfsios_dedup_slash(link); if (cpath) { nev = pvfsios_get_object(this->fsid, cpath, &ref, &creds, PVFS2_LOOKUP_LINK_NO_FOLLOW); free(cpath); } else { nev = -ENOMEM; } *readlen = -1; if (nev < 0) { return errno_to_plfs_error(-nev); } pev = PVFS_sys_getattr(ref, PVFS_ATTR_SYS_ALL_NOHINT, &creds, &resp); if (pev < 0) { return errno_to_plfs_error(-get_err(pev)); } if (resp.attr.objtype != PVFS_TYPE_SYMLINK) { nev = -EINVAL; } else { l = strlen(resp.attr.link_target); cpy = (l < bufsize - 1) ? l : bufsize; memcpy(buf, resp.attr.link_target, cpy); buf[cpy] = 0; *readlen = l; /* need to return length */ /* nev still zero from get_obj call, no need to reset */ } /* XXX: pvfs2fuse didn't release, memory leak? */ PVFS_util_release_sys_attr(&resp.attr); /* frees memory chained off ats */ return errno_to_plfs_error(-nev); }
/** * PVFSIOSDirHandle::~PVFSIOSDirHandle: destruction */ PVFSIOSDirHandle::~PVFSIOSDirHandle() { int lcv; if (this->ncache) { free(this->cache.dirent_array); free(this->cache.stat_err_array); for (lcv = 0 ; lcv < this->ncache ; lcv++) { PVFS_util_release_sys_attr(&this->cache.attr_array[lcv]); } free(this->cache.attr_array); } if (this->locklvl) { pthread_mutex_destroy(&this->poslock); this->locklvl--; } if (this->locklvl) { pthread_cond_destroy(&this->block); this->locklvl--; } }
/** * PVFSIOStore::Access: permission check * * @param path the path we are checking * @param mode the mode to check * @return PLFS_SUCCESS or PLFS_E* on error */ plfs_error_t PVFSIOStore::Access(const char* path, int mode) { char *cpath; PVFS_object_ref ref; PVFS_credentials creds; int nev, pev; PVFS_sysresp_getattr rep; PVFS_uid auid; PVFS_gid agid; PVFS_permissions aperms; cpath = pvfsios_dedup_slash(path); if (cpath) { nev = pvfsios_get_object(this->fsid, cpath, &ref, &creds, PVFS2_LOOKUP_LINK_FOLLOW); free(cpath); } else { nev = -ENOMEM; } if (nev < 0) { return errno_to_plfs_error(-nev); } /* root or exist check */ if (creds.uid == 0 || mode == F_OK) { return PLFS_SUCCESS; } pev = PVFS_sys_getattr(ref, PVFS_ATTR_SYS_ALL_NOHINT, &creds, &rep); if (pev < 0) { return errno_to_plfs_error(get_err(pev)); } #if 0 /* * XXXCDC: we'd really like to call PINT_check_mode, but libpvfs2 * includes don't provide a prototype for it even though it is * present in the lib. does that mean it is a private interface? */ pev = PINT_check_mode(&rep.attr, creds.uid, credis.gid, 0); PVFS_util_release_sys_attr(&rep.attr); /* frees memory chained off ats */ return(get_err(pev)); #endif /* * XXX: pvfs2fuse doesn't call PVFS_util_release_sys_attr on * repl.attr for access. this seems like a memory leak mistake to * me. */ auid = rep.attr.owner; agid = rep.attr.group; aperms = rep.attr.perms; PVFS_util_release_sys_attr(&rep.attr); /* frees memory chained off ats */ if (auid == creds.uid) { if ((mode & R_OK) && (aperms & PVFS_U_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_U_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_U_EXECUTE)) return PLFS_SUCCESS; } /* XXXCDC: doesn't check group list, e.g. getgroups() */ if (agid == creds.gid) { if ((mode & R_OK) && (aperms & PVFS_G_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_G_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_G_EXECUTE)) return PLFS_SUCCESS; } if ((mode & R_OK) && (aperms & PVFS_O_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_O_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_O_EXECUTE)) return PLFS_SUCCESS; return PLFS_EACCES; }
/** * pvfsios_object_stat: stat a pvfs object * * @param rp the object to state * @param cp the creds used to do the stat * @param stb the stat buffer to fill out * @return 0 or -err */ static int pvfsios_object_stat(PVFS_object_ref *rp, PVFS_credentials *cp, struct stat *stb) { PVFS_sysresp_getattr reply; PVFS_sys_attr *ats; int pev, m; memset(stb, 0, sizeof(*stb)); memset(&reply, 0, sizeof(reply)); /* do the RPC */ pev = PVFS_sys_getattr(*rp, PVFS_ATTR_SYS_ALL_NOHINT, cp, &reply); if (pev != 0) { return(get_err(pev)); } ats = &reply.attr; /* set st_blocks, st_size and type bits of st_mode */ switch (ats->objtype) { case PVFS_TYPE_METAFILE: /* a file */ stb->st_mode |= S_IFREG; if (ats->mask & PVFS_ATTR_SYS_SIZE) { stb->st_size = ats->size; stb->st_blocks = (((ats->size + 4095)/4096)*4096)/512; } break; case PVFS_TYPE_SYMLINK: stb->st_mode |= S_IFLNK; if (ats->link_target) stb->st_size = strlen(ats->link_target); break; case PVFS_TYPE_DIRECTORY: stb->st_mode |= S_IFDIR; break; default: /* just leave the values at zero */ break; } stb->st_nlink = 1; stb->st_uid = ats->owner; stb->st_gid = ats->group; stb->st_atime = ats->atime; stb->st_mtime = ats->mtime; stb->st_ctime = ats->ctime; m = 0; /* yuck */ if (ats->perms & PVFS_O_EXECUTE) m |= S_IXOTH; if (ats->perms & PVFS_O_WRITE) m |= S_IWOTH; if (ats->perms & PVFS_O_READ) m |= S_IROTH; if (ats->perms & PVFS_G_EXECUTE) m |= S_IXGRP; if (ats->perms & PVFS_G_WRITE) m |= S_IWGRP; if (ats->perms & PVFS_G_READ) m |= S_IRGRP; if (ats->perms & PVFS_U_EXECUTE) m |= S_IXUSR; if (ats->perms & PVFS_U_WRITE) m |= S_IWUSR; if (ats->perms & PVFS_U_READ) m |= S_IRUSR; if (ats->perms & PVFS_G_SGID) m |= S_ISGID; if (ats->perms & PVFS_U_SUID) m |= S_ISUID; stb->st_mode |= m; stb->st_dev = rp->fs_id; stb->st_ino = rp->handle; stb->st_rdev = 0; stb->st_blksize = 4096; PVFS_util_release_sys_attr(ats); /* frees memory chained off ats */ return(0); }
/** * PVFSIOSDirHandle::Readdir_r: read a PVFS directory * * @param dst a dirent that we can fill out * @param dret we return a pointer to dst here on success, NULL on fail * @return PLFS_SUCCESS or PLFS_E* */ plfs_error_t PVFSIOSDirHandle::Readdir_r(struct dirent *dst, struct dirent **dret) { static int read_size = 32; /* we read this many entries at once */ int dowake, nev, lcv, pev; PVFS_ds_type pvtype; /* init and grab the lock so we can start the operation */ dowake = 0; nev = 0; pthread_mutex_lock(&this->poslock); /* wait for in-progress all i/o to complete */ while (this->in_io) { this->waiting = 1; pthread_cond_wait(&this->block, &this->poslock); } /* hack to make fake . and .. entries */ if (this->dot < 2) { *dret = dst; dst->d_ino = 0; dst->d_reclen = sizeof(struct dirent); dst->d_type = DT_DIR; #ifdef __linux__ dst->d_off = 0; /* XXX??? */ #endif strcpy(dst->d_name, (this->dot == 0) ? "." : ".."); this->dot++; goto done; } /* if the cache is empty and we are not atend, fill cache */ if (this->ncache == 0 && !this->atend) { this->in_io = 1; pthread_mutex_unlock(&this->poslock); memset(&this->cache, 0, sizeof(this->cache)); pev = PVFS_sys_readdirplus(this->ref, this->mydpos, read_size, &this->creds, PVFS_ATTR_SYS_TYPE, &this->cache, NULL); pthread_mutex_lock(&this->poslock); dowake = this->waiting; this->waiting = 0; this->in_io = 0; if (pev < 0) { nev = get_err(pev); goto done; } /* update our position for the next read */ this->mydpos = this->cache.token; /* check for atend, if not then load new cache */ if (this->cache.pvfs_dirent_outcount == 0) { /* check for EOD */ this->atend = 1; } else { this->cachepos = 0; this->ncache = this->cache.pvfs_dirent_outcount; } } /* loaded cache (if possible), see if we hit EOF */ if (this->atend) { /* check for end of directory */ *dret = NULL; goto done; } /* we can return an entry from the cache */ *dret = dst; dst->d_ino = this->cache.dirent_array[this->cachepos].handle; dst->d_reclen = sizeof(struct dirent); if (this->cache.stat_err_array[this->cachepos]) { dst->d_type = DT_UNKNOWN; /* we got an error */ } else { pvtype = this->cache.attr_array[this->cachepos].objtype; if (pvtype == PVFS_TYPE_METAFILE) dst->d_type = DT_REG; else if (pvtype == PVFS_TYPE_DIRECTORY) dst->d_type = DT_DIR; else if (pvtype == PVFS_TYPE_SYMLINK) dst->d_type = DT_LNK; else dst->d_type = DT_UNKNOWN; } #ifdef __linux__ dst->d_off = 0; /* XXX??? */ #endif strcpy(dst->d_name, this->cache.dirent_array[this->cachepos].d_name); this->cachepos++; /* if we used last cached item, dump cache */ if (this->ncache && this->cachepos == this->ncache) { if (this->ncache < read_size) { this->atend = 1; /* atend via short read */ } free(this->cache.dirent_array); free(this->cache.stat_err_array); for (lcv = 0 ; lcv < this->ncache ; lcv++) { PVFS_util_release_sys_attr(&this->cache.attr_array[lcv]); } free(this->cache.attr_array); memset(&this->cache, 0, sizeof(this->cache)); this->ncache = this->cachepos = 0; } done: pthread_mutex_unlock(&this->poslock); if (dowake) pthread_cond_signal(&this->block); return errno_to_plfs_error(-nev); }
int do_list( char *full_path, char *start, int fs_id, struct options *opts) { int i = 0, printed_dot_info = 0; int ret = -1; int pvfs_dirent_incount; char *name = NULL, *cur_file = NULL; PVFS_handle cur_handle; PVFS_sysresp_lookup lk_response; PVFS_sysresp_readdirplus rdplus_response; PVFS_sysresp_getattr getattr_response; PVFS_credentials credentials; PVFS_object_ref ref; PVFS_ds_position token; uint64_t dir_version = 0; double begin = 0., end; subdir *current, *head = NULL, *tail = NULL; name = start; memset(&lk_response,0,sizeof(PVFS_sysresp_lookup)); PVFS_util_gen_credentials(&credentials); if (opts->list_recursive || opts->num_starts > 1) { printf("%s%s:\n",full_path,start); } ret = PVFS_sys_lookup(fs_id, name, &credentials, &lk_response, PVFS2_LOOKUP_LINK_NO_FOLLOW, NULL); if(ret < 0) { PVFS_perror("PVFS_sys_lookup", ret); return -1; } ref.handle = lk_response.ref.handle; ref.fs_id = fs_id; pvfs_dirent_incount = MAX_NUM_DIRENTS; memset(&getattr_response,0,sizeof(PVFS_sysresp_getattr)); if (PVFS_sys_getattr(ref, PVFS_ATTR_SYS_ALL, &credentials, &getattr_response, NULL) == 0) { if ((getattr_response.attr.objtype == PVFS_TYPE_METAFILE) || (getattr_response.attr.objtype == PVFS_TYPE_SYMLINK) || ((getattr_response.attr.objtype == PVFS_TYPE_DIRECTORY) && (opts->list_directory))) { char segment[128] = {0}; PVFS_sysresp_getparent getparent_resp; PINT_remove_base_dir(name, segment, 128); if (strcmp(segment,"") == 0) { snprintf(segment,128,"/"); } if (getattr_response.attr.objtype == PVFS_TYPE_DIRECTORY) { if (PVFS_sys_getparent(ref.fs_id, name, &credentials, &getparent_resp, NULL) == 0) { print_dot_and_dot_dot_info_if_required( getparent_resp.parent_ref); } } if (opts->list_long) { print_entry_attr(ref.handle, segment, &getattr_response.attr, opts); } else { print_entry(segment, ref.handle, ref.fs_id, NULL, 0, opts); } return 0; } } if (do_timing) begin = Wtime(); token = 0; do { memset(&rdplus_response, 0, sizeof(PVFS_sysresp_readdirplus)); ret = PVFS_sys_readdirplus( ref, (!token ? PVFS_READDIR_START : token), pvfs_dirent_incount, &credentials, (opts->list_long) ? PVFS_ATTR_SYS_ALL : PVFS_ATTR_SYS_ALL_NOSIZE, &rdplus_response, NULL); if(ret < 0) { PVFS_perror("PVFS_sys_readdir", ret); return -1; } if (dir_version == 0) { dir_version = rdplus_response.directory_version; } else if (opts->list_verbose) { if (dir_version != rdplus_response.directory_version) { fprintf(stderr, "*** directory changed! listing may " "not be correct\n"); dir_version = rdplus_response.directory_version; } } if (!printed_dot_info) { /* the list_all option prints files starting with .; the almost_all option skips the '.', '..' printing */ print_dot_and_dot_dot_info_if_required(ref); printed_dot_info = 1; } for(i = 0; i < rdplus_response.pvfs_dirent_outcount; i++) { cur_file = rdplus_response.dirent_array[i].d_name; cur_handle = rdplus_response.dirent_array[i].handle; print_entry(cur_file, cur_handle, fs_id, &rdplus_response.attr_array[i], rdplus_response.stat_err_array[i], opts); PVFS_sys_attr *attr = &rdplus_response.attr_array[i]; if(attr->objtype == PVFS_TYPE_DIRECTORY && opts->list_recursive) { int path_len = strlen(start) + strlen(cur_file) + 1; current = (subdir *) malloc(sizeof(subdir)); /* Prevent duplicate slashes in path */ if(start[strlen(start)-1] == '/') { current->path = (char *) malloc(path_len); snprintf(current->path,path_len,"%s%s",start,cur_file); } else { current->path = (char *) malloc(path_len + 1); snprintf(current->path,path_len+1,"%s/%s",start,cur_file); } /* Update linked list of subdirectories to recurse */ current->next = NULL; if(!head) { head = current; tail = current; } else { tail->next = current; tail = current; } } } token = rdplus_response.token; if (rdplus_response.pvfs_dirent_outcount) { free(rdplus_response.dirent_array); rdplus_response.dirent_array = NULL; free(rdplus_response.stat_err_array); rdplus_response.stat_err_array = NULL; for (i = 0; i < rdplus_response.pvfs_dirent_outcount; i++) { if (rdplus_response.attr_array) { PVFS_util_release_sys_attr(&rdplus_response.attr_array[i]); } } free(rdplus_response.attr_array); rdplus_response.attr_array = NULL; } } while(rdplus_response.pvfs_dirent_outcount == pvfs_dirent_incount); if (do_timing) { end = Wtime(); printf("PVFS_sys_readdirplus took %g msecs\n", (end - begin)); } if (rdplus_response.pvfs_dirent_outcount) { free(rdplus_response.dirent_array); rdplus_response.dirent_array = NULL; free(rdplus_response.stat_err_array); rdplus_response.stat_err_array = NULL; for (i = 0; i < rdplus_response.pvfs_dirent_outcount; i++) { if (rdplus_response.attr_array) { PVFS_util_release_sys_attr(&rdplus_response.attr_array[i]); } } free(rdplus_response.attr_array); rdplus_response.attr_array = NULL; } if (opts->list_recursive) { current = head; while(current) { printf("\n"); do_list(full_path,current->path,fs_id,opts); current = current->next; free(head->path); free(head); head = current; } } return 0; }