static int pgfuse_getattr( const char *path, struct stat *stbuf ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "GetAttrs '%s' on '%s', thread #%u", path, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); memset( stbuf, 0, sizeof( struct stat ) ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( data->verbose ) { syslog( LOG_DEBUG, "Id for %s '%s' is %"PRIi64", thread #%u", S_ISDIR( meta.mode ) ? "dir" : "file", path, id, THREAD_ID ); } convert_meta_to_stbuf( stbuf, &meta, data, id ); PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
inline void emptyAllRecvBufs(){ struct sembuf sb; int i; int j,ret; union semun { int val; struct semid_ds *buf; ushort array[1]; } arg; for(i=0;i<sysvshmContext->nodesize;i++){ if(i != sysvshmContext->noderank){ sharedBufData *recvBuf = &(sysvshmContext->recvBufs[i]); if(recvBuf->header->count > 0){ #if SYSVSHM_STATS sysvshmContext->lockRecvCount++; #endif ACQUIRE(i); if(semop(recvBuf->semid, &sb, 1)>=0) { MACHSTATE1(3,"emptyRecvBuf to be called for rank %d",i); emptyRecvBuf(recvBuf); RELEASE(i); CmiAssert((semop(recvBuf->semid, &sb, 1))>=0); } } } } };
static int pgfuse_open( const char *path, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; PgMeta meta; int64_t id; int64_t res; PGconn *conn; if( data->verbose ) { char *s = flags_to_string( fi->flags ); syslog( LOG_INFO, "Open '%s' on '%s' with flags '%s', thread #%u", path, data->mountpoint, s, THREAD_ID ); if( *s != '<' ) free( s ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( data->verbose ) { syslog( LOG_DEBUG, "Id for file '%s' to open is %"PRIi64", thread #%u", path, id, THREAD_ID ); } if( S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EISDIR; } if( data->read_only ) { if( ( fi->flags & O_ACCMODE ) != O_RDONLY ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } } if( !data->noatime ) { meta.atime = now( ); } res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } fi->fh = id; PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_truncate( const char* path, off_t offset ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; int res; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Truncate of '%s' to size '%jd' on '%s', thread #%u", path, offset, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EISDIR; } if( data->verbose ) { syslog( LOG_DEBUG, "Id of file '%s' to be truncated is %"PRIi64", thread #%u", path, id, THREAD_ID ); } if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } res = psql_truncate( conn, data->block_size, id, path, offset ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } meta.size = offset; meta.ctime = now( ); meta.mtime = meta.ctime; res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
// Assuming ACQUIRE can throw an exception, we must put this logic // somewhere outside of the constructor. In C++, the destructor won't be // run if the constructor didn't complete. void Initialize(const unsigned int numElements, PTRARRAYREF* pValues) { WRAPPER_NO_CONTRACT; _ASSERTE(m_numAcquired == 0); m_numElements = numElements; m_pValues = pValues; for (unsigned int i=0; i<m_numElements; i++) { TYPE value = (TYPE) (*m_pValues)->GetAt(i); ACQUIRE(value); m_numAcquired++; } }
static int pgfuse_ftruncate( const char *path, off_t offset, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; int res; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Truncate of '%s' to size '%jd' on '%s', thread #%u", path, offset, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); if( fi->fh == 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EBADF; } id = psql_read_meta( conn, fi->fh, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } res = psql_truncate( conn, data->block_size, fi->fh, path, offset ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } meta.size = offset; meta.ctime = now( ); meta.mtime = meta.ctime; res = psql_write_meta( conn, fi->fh, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_read( const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int res; PGconn *conn; int64_t tmp; PgMeta meta; if( data->verbose ) { syslog( LOG_INFO, "Read to '%s' from offset %jd, size %zu on '%s', thread #%u", path, offset, size, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); if( fi->fh == 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EBADF; } res = psql_read_buf( conn, data->block_size, fi->fh, path, buf, offset, size, data->verbose ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } if( !data->noatime ) { tmp = psql_read_meta( conn, fi->fh, path, &meta ); if( tmp < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return tmp; } meta.atime = now( ); tmp = psql_write_meta( conn, fi->fh, path, meta ); if( tmp < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return tmp; } } PSQL_COMMIT( conn ); RELEASE( conn ); return res; }
static int pgfuse_unlink( const char *path ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; int res; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Remove file '%s' on '%s', thread #%u", path, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EPERM; } if( data->verbose ) { syslog( LOG_DEBUG, "Id of file '%s' to be removed is %"PRIi64", thread #%u", path, id, THREAD_ID ); } if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } res = psql_delete_file( conn, id, path ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } // TODO: update ctime/mtime of parent directory, have functions to // get the parent directory PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
inline void flushAllSendQs(){ struct sembuf sb; int i=0; for(i=0;i<sysvshmContext->nodesize;i++){ if(i != sysvshmContext->noderank && sysvshmContext->sendQs[i]->numEntries > 0){ ACQUIRE(sysvshmContext->noderank); if(semop(sysvshmContext->sendBufs[i].semid, &sb, 1)>=0) { MACHSTATE1(3,"flushSendQ %d",i); flushSendQ(i); RELEASE(sysvshmContext->noderank); CmiAssert(semop(sysvshmContext->sendBufs[i].semid, &sb, 1)>=0); } } } };
static int pgfuse_readdir( const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int id; int res; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Readdir '%s' on '%s', thread #%u", path, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); filler( buf, ".", NULL, 0 ); filler( buf, "..", NULL, 0 ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } res = psql_readdir( conn, id, buf, filler ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } if( !data->noatime ) { meta.atime = now( ); res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } } PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_readlink( const char *path, char *buf, size_t size ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; int res; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Dereferencing symlink '%s' on '%s', thread #%u", path, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( !S_ISLNK( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOENT; } if( size < meta.size + 1 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } res = psql_read_buf( conn, data->block_size, id, path, buf, 0, meta.size, data->verbose ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } buf[meta.size] = '\0'; PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_chown( const char *path, uid_t uid, gid_t gid ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; int res; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Chown on '%s' to uid '%d' and gid '%d' on '%s', thread #%u", path, (unsigned int)uid, (unsigned int)gid, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } meta.uid = uid; meta.gid = gid; meta.ctime = now( ); res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return res; }
static int pgfuse_utimens( const char *path, const struct timespec tv[2] ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; int res; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Utimens on '%s' to access time '%d' and modification time '%d' on '%s', thread #%u", path, (unsigned int)tv[0].tv_sec, (unsigned int)tv[1].tv_sec, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( !data->noatime ) { meta.atime = tv[0]; } meta.mtime = tv[1]; res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_release( const char *path, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; /* nothing to do given the simple transaction model */ if( data->verbose ) { syslog( LOG_INFO, "Releasing '%s' on '%s', thread #%u", path, data->mountpoint, THREAD_ID ); } if( !data->noatime ) { int64_t id; int res; PgMeta meta; PGconn *conn; ACQUIRE( conn ); PSQL_BEGIN( conn ); id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } meta.atime = now( ); res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); } return 0; }
/* lookups of dots are passed down */ static int do_namei(struct file *cwd, const char *path, struct file *result_filp, char *result_name, int *followlinks, int followlastlink) { int error; char next[NAME_MAX +1]; struct file old_space, new_space,tmp_traversal_space; struct file *old_filp = &old_space; struct file *new_filp = &new_space; struct file *tmp_traversal_filp = &tmp_traversal_space; struct file *tmp_swap_filp; struct shortfile release = {0,0}; old_filp->flock_state = FLOCK_STATE_UNLOCKED; new_filp->flock_state = FLOCK_STATE_UNLOCKED; tmp_traversal_filp->flock_state = FLOCK_STATE_UNLOCKED; DPRINTF(SYSHELP_LEVEL,("do_namei: path: %s fl: %d fll: %d\n",path,*followlinks,followlastlink)); nprintf("do_namei: path: \"%s\" fl: %d fll: %d\n", path,*followlinks,followlastlink); /* If we have followed too many symlinks, error ELOOP */ if (*followlinks > LINK_MAX) { errno = ELOOP; return -1; } ASSERTOP(cwd,lookup, directory with no lookup); *old_filp = *cwd; COUNT_COPY; ACQUIRE(old_filp); /* old_filp contains the directory we start at, and * new_filp will contain the name we just traversed. * when we return to top of loop, we copy new_filp into old_filp * when we exit the loop, the resulting filp will be in new_filp */ while(*path) { nprintf("path before : \"%s\" \n",path); path = getNextComponent(path,next); nprintf("NEXT: \"%s\" path[0]: %d \n",next,*path); /* printf("path: \"%20s\" next: \"%s\"\n",path,next); */ if (EQDOTDOT(next)) { /* check traversal of /.. */ if (FILPEQ(old_filp,__current->root)) { next[1] = (char)0; /* make it . */ } else /* check traversal of /mnt/.. takes you back to / */ if (mounttraversalb(old_filp,&tmp_traversal_filp) == 0) { RELEASE(old_filp); COUNT_COPY; *old_filp = *tmp_traversal_filp; ACQUIRE(old_filp); PRSHORTFILP("NEW POINT",old_filp,"\n"); /* fall through, old_filp points to /mnt, and next is .. * so the later operations will handle the lookup of .. */ } } if (EQDOT(next)) { nprintf("in namei shortcircuiting . path: \"%s\"\n",path); tmp_swap_filp = old_filp; old_filp = new_filp; new_filp = tmp_swap_filp; goto check_forward_mount; } if (!(S_ISDIR(old_filp->f_mode))) { nprintf("old_filp: ENOTDIR\n"); errno = ENOTDIR; RELEASE(old_filp); return -1; } ASSERTOP(old_filp,lookup,no lookup operation in traversal of a path); #ifdef CACHEDLOOKUP error = namei_lookup(old_filp, next, new_filp); #else /* Do the actual lookup */ nprintf("#OLD0: %s %d %08x\n",next,old_filp->f_ino, old_filp->f_mode); error = DOOP(old_filp,lookup,(old_filp,next,new_filp)); nprintf("#OLD1: %s %d %08x\n",next,old_filp->f_ino, old_filp->f_mode); #endif /* namei_lookup */ if (error) { RELEASE(old_filp); nprintf("LOOKUP old_filp %s failed errno: %s\n",next,strerror(errno)); return -1; } PRSHORTFILP("LOOKUP ",old_filp," "); nprintf("#NAME: %p %s %d %08x\n",new_filp,next,new_filp->f_ino, new_filp->f_mode); nprintf("#OLD2: %p %s %d %08x\n",old_filp,next,old_filp->f_ino, old_filp->f_mode); PRSHORTFILP(" RETURNED:",new_filp,"\n"); /* here you want to check if new_filp is a symbolic link, it is you want to call readlink, then return namei on the new path we got from readlink. followlink will be an integer denoting how many links we have followed. if 0 means we dont follow links. */ if (S_ISLNK(new_filp->f_mode)) { char linkpath[PATH_MAX + 1]; struct file *link_filp; nprintf("%d) SYMLINK new_filp %d is symlink remaining path: \"%s\" fll %d\n", *followlinks,new_filp->f_ino,path,followlastlink); /* if this is the last path element, and we dont followlastlink */ if (*path == 0 && followlastlink == 0) { RELEASE(old_filp); break; } ASSERTOP(new_filp,readlink,no readlink on a symlink filp); nprintf("3#NAME: %p %s %d %08x\n",new_filp,next,new_filp->f_ino, new_filp->f_mode); if ((error = DOOP(new_filp,readlink,(new_filp,linkpath,PATH_MAX))) == -1) { RELEASE(old_filp); RELEASE(new_filp); return -1; } if (error >= 0) linkpath[error] = 0; nprintf("5#NAME: %p %s %d %08x\n",new_filp,next,new_filp->f_ino, new_filp->f_mode); RELEASE(new_filp); nprintf("6#NAME: %p %s %d %08x\n",new_filp,next,new_filp->f_ino, new_filp->f_mode); nprintf("%d) following symlink path: %s\n",*followlinks,linkpath); /* since new_filp is a symlink it could only have come from a lookup */ (*followlinks)++; if (linkpath[0] == '/') link_filp = (__current->root); else link_filp = old_filp; error = do_namei(link_filp, linkpath, new_filp, result_name, followlinks, followlastlink); RELEASE(old_filp); nprintf("%d) error: %d PATH LEFT: %s, from %d, %x\n", *followlinks,error,path,new_filp->f_ino, new_filp->f_mode); if (error) { nprintf("DO_NAMEI failed errno: %s\n",strerror(errno)); return -1; } goto next; //return error; } RELEASE(old_filp); check_forward_mount: if (mounttraversalf(new_filp,&tmp_traversal_filp) == 0) { nprintf("traversing mount point ->, remaining: \"%s\"\n",path); RELEASE(new_filp); COUNT_COPY; *new_filp = *tmp_traversal_filp; ACQUIRE(new_filp); #ifdef NAMEIDEBUG PRSHORTFILP("NEW POINT",new_filp,"\n"); #endif next[0] = '.'; next[1] = 0; } next: if (*path) { /* efficient way of doing: *old_filp = *new_filp, */ tmp_swap_filp = old_filp; old_filp = new_filp; new_filp = tmp_swap_filp; } } #ifdef NAMEIDEBUG nprintf("#LAST NAME: %s %d %08x\n",next,new_filp->f_ino, new_filp->f_mode); #endif COUNT_COPY; *result_filp = *new_filp; if (result_name) { strcpy(result_name,next); } return 0; }
static int pgfuse_statfs( const char *path, struct statvfs *buf ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; PGconn *conn; int64_t blocks_total, blocks_used, blocks_free, blocks_avail; int64_t files_total, files_used, files_free, files_avail; int res; int i; size_t nof_locations = MAX_TABLESPACE_OIDS; char *location[MAX_TABLESPACE_OIDS]; FILE *mtab; struct mntent *m; struct mntent mnt; char strings[MTAB_BUFFER_SIZE]; char *prefix; int prefix_len; if( data->verbose ) { syslog( LOG_INFO, "Statfs called on '%s', thread #%u", data->mountpoint, THREAD_ID ); } memset( buf, 0, sizeof( struct statvfs ) ); ACQUIRE( conn ); PSQL_BEGIN( conn ); /* blocks */ res = psql_get_tablespace_locations( conn, location, &nof_locations, data->verbose ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } /* transform them and especially resolve symlinks */ for( i = 0; i < nof_locations; i++ ) { char *old_path = location[i]; char *new_path = realpath( old_path, NULL ); if( new_path == NULL ) { /* do nothing, most likely a permission problem */ syslog( LOG_ERR, "realpath for '%s' failed: %s, pgfuse mount point '%s', thread #%u", old_path, strerror( errno ), data->mountpoint, THREAD_ID ); } else { location[i] = new_path; free( old_path ); } } blocks_free = INT64_MAX; blocks_avail = INT64_MAX; /* iterate over mount entries and try to match to the tablespace locations */ mtab = setmntent( MTAB_FILE, "r" ); while( ( m = getmntent_r( mtab, &mnt, strings, sizeof( strings ) ) ) != NULL ) { struct statfs fs; /* skip filesystems without mount point */ if( mnt.mnt_dir == NULL ) continue; /* skip filesystems which are not a prefix of one of the tablespace locations */ prefix = NULL; prefix_len = 0; for( i = 0; i < nof_locations; i++ ) { if( strncmp( mnt.mnt_dir, location[i], strlen( mnt.mnt_dir ) ) == 0 ) { if( strlen( mnt.mnt_dir ) > prefix_len ) { prefix_len = strlen( mnt.mnt_dir ); prefix = strdup( mnt.mnt_dir ); blocks_free = INT64_MAX; blocks_avail = INT64_MAX; } } } if( prefix == NULL ) continue; /* get data of file system */ res = statfs( prefix, &fs ); if( res < 0 ) { syslog( LOG_ERR, "statfs on '%s' failed: %s, pgfuse mount point '%s', thread #%u", prefix, strerror( errno ), data->mountpoint, THREAD_ID ); return res; } if( data->verbose ) { syslog( LOG_DEBUG, "Checking mount point '%s' for free disk space, now %jd, was %jd, pgfuse mount point '%s', thread #%u", prefix, fs.f_bfree, blocks_free, data->mountpoint, THREAD_ID ); } /* take the smallest available disk space free (worst case the first one * to overflow one of the tablespaces) */ if( fs.f_bfree * fs.f_frsize < blocks_free * data->block_size ) { blocks_free = fs.f_bfree * fs.f_frsize / data->block_size; } if( fs.f_bavail * fs.f_frsize < blocks_avail * data->block_size ) { blocks_avail = fs.f_bavail * fs.f_frsize / data->block_size; } if( prefix ) free( prefix ); } endmntent( mtab ); for( i = 0; i < nof_locations; i++ ) { if( location[i] ) free( location[i] ); } blocks_used = psql_get_fs_blocks_used( conn ); if( blocks_used < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return blocks_used; } blocks_total = blocks_avail + blocks_used; blocks_free = blocks_avail; /* inodes */ /* no restriction on the number of files storable, we could add some limits later */ files_total = INT64_MAX; files_used = psql_get_fs_files_used( conn ); if( files_used < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return files_used; } files_free = files_total - files_used; files_avail = files_free; if( data->verbose ) { syslog( LOG_DEBUG, "Stats for '%s' are (%jd blocks total, %jd used, %jd free, " "%"PRId64" inodes total, %"PRId64" inodes used, %"PRId64" inodes free, thread #%u", data->mountpoint, blocks_total, blocks_used, blocks_free, files_total, files_used, files_free, THREAD_ID ); } /* fill statfs structure */ /* Note: blocks have to be retrning as units of f_frsize * f_favail, f_fsid and f_flag are currently ignored by FUSE ? */ buf->f_bsize = data->block_size; buf->f_frsize = data->block_size; buf->f_blocks = blocks_total; buf->f_bfree = blocks_free; buf->f_bavail = blocks_avail; buf->f_files = files_total; buf->f_ffree = files_free; buf->f_favail = files_avail; buf->f_fsid = 0x4FE3A364; if( data->read_only ) { buf->f_flag |= ST_RDONLY; } buf->f_namemax = MAX_FILENAME_LENGTH; PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_write( const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t tmp; int res; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Write to '%s' from offset %jd, size %zu on '%s', thread #%u", path, offset, size, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); if( fi->fh == 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EBADF; } if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EBADF; } tmp = psql_read_meta( conn, fi->fh, path, &meta ); if( tmp < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return tmp; } if( offset + size > meta.size ) { meta.size = offset + size; } meta.mtime = now( ); res = psql_write_buf( conn, data->block_size, fi->fh, path, buf, offset, size, data->verbose ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } if( res != size ) { syslog( LOG_ERR, "Write size mismatch in file '%s' on mountpoint '%s', expected '%d' to be written, but actually wrote '%d' bytes! Data inconistency!", path, data->mountpoint, (unsigned int)size, res ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EIO; } res = psql_write_meta( conn, fi->fh, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return size; }
static int pgfuse_symlink( const char *from, const char *to ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; char *copy_to; char *parent_path; char *symlink; int64_t parent_id; int res; int64_t id; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Symlink from '%s' to '%s' on '%s', thread #%u", from, to, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); copy_to = strdup( to ); if( copy_to == NULL ) { syslog( LOG_ERR, "Out of memory in Symlink '%s'!", to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } parent_path = dirname( copy_to ); parent_id = psql_read_meta_from_path( conn, parent_path, &meta ); if( parent_id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return parent_id; } if( !S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOENT; } if( data->verbose ) { syslog( LOG_DEBUG, "Parent_id for symlink '%s' is %"PRIi64", thread #%u", to, parent_id, THREAD_ID ); } free( copy_to ); copy_to = strdup( to ); if( copy_to == NULL ) { syslog( LOG_ERR, "Out of memory in Symlink '%s'!", to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } if( data->read_only ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } symlink = basename( copy_to ); meta.size = strlen( from ); /* size = length of path */ meta.mode = 0777 | S_IFLNK; /* symlinks have no modes per se */ /* TODO: use FUSE context */ meta.uid = fuse_get_context( )->uid; meta.gid = fuse_get_context( )->gid; meta.ctime = now( ); meta.mtime = meta.ctime; meta.atime = meta.ctime; res = psql_create_file( conn, parent_id, to, symlink, meta ); if( res < 0 ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } id = psql_read_meta_from_path( conn, to, &meta ); if( id < 0 ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } res = psql_write_buf( conn, data->block_size, id, to, from, 0, strlen( from ), data->verbose ); if( res < 0 ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } if( res != strlen( from ) ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EIO; } // TODO: update ctime/mtime of parent directory, have functions to // get the parent directory free( copy_to ); PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_mkdir( const char *path, mode_t mode ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; char *copy_path; char *parent_path; char *new_dir; int64_t parent_id; int res; PgMeta meta; PGconn *conn; if( data->verbose ) { syslog( LOG_INFO, "Mkdir '%s' in mode '%o' on '%s', thread #%u", path, (unsigned int)mode, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } copy_path = strdup( path ); if( copy_path == NULL ) { syslog( LOG_ERR, "Out of memory in Mkdir '%s'!", path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } parent_path = dirname( copy_path ); parent_id = psql_read_meta_from_path( conn, parent_path, &meta ); if( parent_id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return parent_id; } if( !S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOENT; } if( data->verbose ) { syslog( LOG_DEBUG, "Parent_id for new dir '%s' is %"PRIi64", thread #%u", path, parent_id, THREAD_ID ); } free( copy_path ); copy_path = strdup( path ); if( copy_path == NULL ) { free( parent_path ); syslog( LOG_ERR, "Out of memory in Mkdir '%s'!", path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } new_dir = basename( copy_path ); meta.size = 0; meta.mode = mode | S_IFDIR; /* S_IFDIR is not set by fuse */ meta.uid = fuse_get_context( )->uid; meta.gid = fuse_get_context( )->gid; meta.ctime = now( ); meta.mtime = meta.ctime; meta.atime = meta.ctime; res = psql_create_dir( conn, parent_id, path, new_dir, meta ); if( res < 0 ) { free( copy_path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } free( copy_path ); PSQL_COMMIT( conn ); RELEASE( conn ); return 0; }
static int pgfuse_rename( const char *from, const char *to ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; PGconn *conn; int res; int64_t from_id; int64_t to_id; PgMeta from_meta; PgMeta to_meta; char *copy_to; char *parent_path; int64_t to_parent_id; PgMeta to_parent_meta; char *rename_to; if( data->verbose ) { syslog( LOG_INFO, "Renaming '%s' to '%s' on '%s', thread #%u", from, to, data->mountpoint, THREAD_ID ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); from_id = psql_read_meta_from_path( conn, from, &from_meta ); if( from_id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return from_id; } to_id = psql_read_meta_from_path( conn, to, &to_meta ); if( to_id < 0 && to_id != -ENOENT ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return to_id; } /* destination already exists */ if( to_id > 0 ) { /* destination is a file */ if( S_ISREG( to_meta.mode ) ) { if( strcmp( from, to ) == 0 ) { /* source equal to destination? This should succeed */ PSQL_ROLLBACK( conn ); RELEASE( conn ); return 0; } else { /* otherwise make source file disappear and * destination file contain the same data * as the source one (preferably atomic because * of rename/lockfile tricks) */ res = psql_rename_to_existing_file( conn, from_id, to_id, from, to ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } PSQL_COMMIT( conn ); RELEASE( conn ); return res; } } /* TODO: handle all other cases */ PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EINVAL; } copy_to = strdup( to ); if( copy_to == NULL ) { syslog( LOG_ERR, "Out of memory in Rename '%s'!", to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } parent_path = dirname( copy_to ); to_parent_id = psql_read_meta_from_path( conn, parent_path, &to_parent_meta ); if( to_parent_id < 0 ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return to_parent_id; } if( !S_ISDIR( to_parent_meta.mode ) ) { syslog( LOG_ERR, "Weird situation in Rename, '%s' expected to be a directory!", parent_path ); free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EIO; } free( copy_to ); copy_to = strdup( to ); if( copy_to == NULL ) { syslog( LOG_ERR, "Out of memory in Rename '%s'!", to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } if( data->read_only ) { free( copy_to ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } rename_to = basename( copy_to ); res = psql_rename( conn, from_id, from_meta.parent_id, to_parent_id, rename_to, from, to ); free( copy_to ); PSQL_COMMIT( conn ); RELEASE( conn ); return res; }
static int pgfuse_create( const char *path, mode_t mode, struct fuse_file_info *fi ) { PgFuseData *data = (PgFuseData *)fuse_get_context( )->private_data; int64_t id; PgMeta meta; char *copy_path; char *parent_path; char *new_file; int64_t parent_id; int64_t res; PGconn *conn; if( data->verbose ) { char *s = flags_to_string( fi->flags ); syslog( LOG_INFO, "Create '%s' in mode '%o' on '%s' with flags '%s', thread #%u", path, mode, data->mountpoint, s, THREAD_ID ); if( *s != '<' ) free( s ); } ACQUIRE( conn ); PSQL_BEGIN( conn ); if( data->read_only ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EROFS; } id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 && id != -ENOENT ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return id; } if( id >= 0 ) { if( data->verbose ) { syslog( LOG_DEBUG, "Id for dir '%s' is %"PRIi64", thread #%u", path, id, THREAD_ID ); } if( S_ISDIR( meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EISDIR; } if( ( fi->flags & O_CREAT ) && (fi->flags & O_EXCL ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -EEXIST; } res = psql_truncate( conn, data->block_size, id, path, 0 ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } meta.size = 0; res = psql_write_meta( conn, id, path, meta ); if( res < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } } copy_path = strdup( path ); if( copy_path == NULL ) { syslog( LOG_ERR, "Out of memory in Create '%s'!", path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } parent_path = dirname( copy_path ); parent_id = psql_read_meta_from_path( conn, parent_path, &meta ); if( parent_id < 0 ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return parent_id; } if( !S_ISDIR(meta.mode ) ) { PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOENT; } if( data->verbose ) { syslog( LOG_DEBUG, "Parent_id for new file '%s' in dir '%s' is %"PRIi64", thread #%u", path, parent_path, parent_id, THREAD_ID ); } free( copy_path ); copy_path = strdup( path ); if( copy_path == NULL ) { free( parent_path ); syslog( LOG_ERR, "Out of memory in Create '%s'!", path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return -ENOMEM; } new_file = basename( copy_path ); meta.size = 0; meta.mode = mode; meta.uid = fuse_get_context( )->uid; meta.gid = fuse_get_context( )->gid; meta.ctime = now( ); meta.mtime = meta.ctime; meta.atime = meta.ctime; res = psql_create_file( conn, parent_id, path, new_file, meta ); if( res < 0 ) { free( copy_path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } id = psql_read_meta_from_path( conn, path, &meta ); if( id < 0 ) { free( copy_path ); PSQL_ROLLBACK( conn ); RELEASE( conn ); return res; } if( data->verbose ) { syslog( LOG_DEBUG, "Id for new file '%s' is %"PRIi64", thread #%u", path, id, THREAD_ID ); } fi->fh = id; free( copy_path ); PSQL_COMMIT( conn ); RELEASE( conn ); return res; }