| /* |
| * Copyright (c) 2000-2002 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <dirent.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <xfs/xfs.h> |
| #include <xfs/jdm.h> |
| |
| #include "config.h" |
| |
| #include "types.h" |
| #include "util.h" |
| #include "mlog.h" |
| #include "getdents.h" |
| |
| extern size_t pgsz; |
| |
| int |
| write_buf( char *bufp, |
| size_t bufsz, |
| void *contextp, |
| gwbfp_t get_write_buf_funcp, |
| wfp_t write_funcp ) |
| { |
| char *mbufp; /* buffer obtained from manager */ |
| size_t mbufsz;/* size of buffer obtained from manager */ |
| |
| while ( bufsz ) { |
| int rval; |
| |
| assert( bufsz > 0 ); |
| mbufp = ( *get_write_buf_funcp )( contextp, bufsz, &mbufsz ); |
| assert( mbufsz <= bufsz ); |
| if ( bufp ) { |
| (void)memcpy( ( void * )mbufp, ( void * )bufp, mbufsz ); |
| } else { |
| (void)memset( ( void * )mbufp, 0, mbufsz ); |
| } |
| rval = ( * write_funcp )( contextp, mbufp, mbufsz ); |
| if ( rval ) { |
| return rval; |
| } |
| if ( bufp ) { |
| bufp += mbufsz; |
| } |
| bufsz -= mbufsz; |
| } |
| |
| return 0; |
| } |
| |
| int |
| read_buf( char *bufp, |
| size_t bufsz, |
| void *contextp, |
| rfp_t read_funcp, |
| rrbfp_t return_read_buf_funcp, |
| int *statp ) |
| { |
| char *mbufp; /* manager's buffer pointer */ |
| size_t mbufsz; /* size of manager's buffer */ |
| int nread; |
| |
| nread = 0; |
| *statp = 0; |
| while ( bufsz ) { |
| mbufp = ( * read_funcp )( contextp, bufsz, &mbufsz, statp ); |
| if ( *statp ) { |
| break; |
| } |
| assert( mbufsz <= bufsz ); |
| if ( bufp ) { |
| ( void )memcpy( (void *)bufp, (void *)mbufp, mbufsz ); |
| bufp += mbufsz; |
| } |
| bufsz -= mbufsz; |
| nread += ( int )mbufsz; |
| ( * return_read_buf_funcp )( contextp, mbufp, mbufsz ); |
| } |
| |
| return nread; |
| } |
| |
| char |
| *strncpyterm( char *s1, char *s2, size_t n ) |
| { |
| char *rval; |
| |
| if ( n < 1 ) return 0; |
| rval = strncpy( s1, s2, n ); |
| s1[ n - 1 ] = 0; |
| return rval; |
| } |
| |
| int |
| bigstat_iter( jdm_fshandle_t *fshandlep, |
| int fsfd, |
| int selector, |
| xfs_ino_t start_ino, |
| bstat_cbfp_t fp, |
| void * cb_arg1, |
| bstat_seekfp_t seekfp, |
| void * seek_arg1, |
| int *statp, |
| bool_t ( pfp )( int ), |
| xfs_bstat_t *buf, |
| size_t buflenin ) |
| { |
| __s32 buflenout; |
| xfs_ino_t lastino; |
| int saved_errno; |
| int bulkstatcnt; |
| xfs_fsop_bulkreq_t bulkreq; |
| |
| /* stat set with return from callback func |
| */ |
| *statp = 0; |
| |
| /* NOT COOL: open will affect root dir's access time |
| */ |
| |
| mlog( MLOG_NITTY, |
| "bulkstat iteration initiated: start_ino == %llu\n", |
| start_ino ); |
| |
| /* quirk of the interface: I want to play in terms of the |
| * ino to begin with, and ino 0 is not used. so, ... |
| */ |
| if ( start_ino > 0 ) { |
| lastino = start_ino - 1; |
| } else { |
| lastino = 0; |
| } |
| mlog( MLOG_NITTY + 1, |
| "calling bulkstat\n" ); |
| |
| bulkstatcnt = 0; |
| bulkreq.lastip = (__u64 *)&lastino; |
| bulkreq.icount = buflenin; |
| bulkreq.ubuffer = buf; |
| bulkreq.ocount = &buflenout; |
| while (!ioctl(fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) { |
| xfs_bstat_t *p; |
| xfs_bstat_t *endp; |
| |
| if ( buflenout == 0 ) { |
| mlog( MLOG_NITTY + 1, |
| "bulkstat returns buflen %d\n", |
| buflenout ); |
| return 0; |
| } |
| mlog( MLOG_NITTY + 1, |
| "bulkstat returns buflen %d ino %llu\n", |
| buflenout, |
| buf->bs_ino ); |
| for ( p = buf, endp = buf + buflenout ; p < endp ; p++ ) { |
| int rval; |
| |
| if ( p->bs_ino == 0 ) |
| continue; |
| |
| if ( !p->bs_nlink || !p->bs_mode ) { |
| /* inode being modified, get synced data */ |
| mlog( MLOG_NITTY + 1, |
| "ino %llu needed second bulkstat\n", |
| p->bs_ino ); |
| |
| if( bigstat_one( fsfd, p->bs_ino, p ) < 0 ) { |
| mlog( MLOG_WARNING, |
| _("failed to get bulkstat information for inode %llu\n"), |
| p->bs_ino ); |
| continue; |
| } |
| if ( !p->bs_nlink || !p->bs_mode || !p->bs_ino ) { |
| mlog( MLOG_TRACE, |
| _("failed to get valid bulkstat information for inode %llu\n"), |
| p->bs_ino ); |
| continue; |
| } |
| } |
| |
| if ( ( p->bs_mode & S_IFMT ) == S_IFDIR ) { |
| if ( ! ( selector & BIGSTAT_ITER_DIR )){ |
| continue; |
| } |
| } else { |
| if ( ! ( selector & BIGSTAT_ITER_NONDIR )){ |
| continue; |
| } |
| } |
| rval = ( * fp )( cb_arg1, fshandlep, fsfd, p ); |
| if ( rval ) { |
| *statp = rval; |
| return 0; |
| } |
| if ( pfp ) ( pfp )( PREEMPT_PROGRESSONLY ); |
| } |
| |
| if ( pfp && (++bulkstatcnt % 10) == 0 && |
| ( pfp )( PREEMPT_FULL )) { |
| return EINTR; |
| } |
| |
| if (seekfp) { |
| lastino = seekfp(seek_arg1, lastino); |
| if (lastino == INO64MAX) { |
| mlog( MLOG_DEBUG, |
| "bulkstat seeked to EOS\n"); |
| return 0; |
| } |
| |
| mlog( MLOG_DEBUG, |
| "bulkstat seeked to %llu\n", lastino); |
| |
| lastino = (lastino > 0) ? lastino - 1 : 0; |
| } |
| |
| mlog( MLOG_NITTY + 1, |
| "calling bulkstat\n" ); |
| } |
| |
| saved_errno = errno; |
| |
| mlog( MLOG_NORMAL, |
| _("syssgi( SGI_FS_BULKSTAT ) on fsroot failed: %s\n"), |
| strerror( saved_errno )); |
| |
| return saved_errno; |
| } |
| |
| /* ARGSUSED */ |
| int |
| bigstat_one( int fsfd, |
| xfs_ino_t ino, |
| xfs_bstat_t *statp ) |
| { |
| xfs_fsop_bulkreq_t bulkreq; |
| int count = 0; |
| |
| assert( ino > 0 ); |
| bulkreq.lastip = (__u64 *)&ino; |
| bulkreq.icount = 1; |
| bulkreq.ubuffer = statp; |
| bulkreq.ocount = &count; |
| return xfsctl(NULL, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq); |
| } |
| |
| /* call the given callback for each inode group in the filesystem. |
| */ |
| #define INOGRPLEN 256 |
| int |
| inogrp_iter( int fsfd, |
| int ( * fp )( void *arg1, |
| int fsfd, |
| xfs_inogrp_t *inogrp ), |
| void * arg1, |
| int *statp ) |
| { |
| xfs_ino_t lastino; |
| int inogrpcnt; |
| xfs_inogrp_t *igrp; |
| xfs_fsop_bulkreq_t bulkreq; |
| |
| /* stat set with return from callback func */ |
| *statp = 0; |
| |
| igrp = malloc(INOGRPLEN * sizeof(xfs_inogrp_t)); |
| if (!igrp) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, |
| _("malloc of stream context failed (%d bytes): %s\n"), |
| INOGRPLEN * sizeof(xfs_inogrp_t), |
| strerror(errno)); |
| return -1; |
| } |
| |
| lastino = 0; |
| inogrpcnt = 0; |
| bulkreq.lastip = (__u64 *)&lastino; |
| bulkreq.icount = INOGRPLEN; |
| bulkreq.ubuffer = igrp; |
| bulkreq.ocount = &inogrpcnt; |
| while (!ioctl(fsfd, XFS_IOC_FSINUMBERS, &bulkreq)) { |
| xfs_inogrp_t *p, *endp; |
| |
| if ( inogrpcnt == 0 ) { |
| free(igrp); |
| return 0; |
| } |
| for ( p = igrp, endp = igrp + inogrpcnt ; p < endp ; p++ ) { |
| int rval; |
| |
| rval = ( * fp )( arg1, fsfd, p ); |
| if ( rval ) { |
| *statp = rval; |
| free(igrp); |
| return 0; |
| } |
| } |
| } |
| |
| free(igrp); |
| return errno; |
| } |
| |
| /* calls the callback for every entry in the directory specified |
| * by the stat buffer. supplies the callback with a file system |
| * handle and a stat buffer, and the name from the dirent. |
| * |
| * NOTE: does NOT invoke callback for "." or ".."! |
| * |
| * if the callback returns non-zero, returns 1 with cbrval set to the |
| * callback's return value. if syscall fails, returns -1 with errno set. |
| * otherwise returns 0. |
| * |
| * caller may supply a dirent buffer. if not, will malloc one |
| */ |
| int |
| diriter( jdm_fshandle_t *fshandlep, |
| int fsfd, |
| xfs_bstat_t *statp, |
| int ( *cbfp )( void *arg1, |
| jdm_fshandle_t *fshandlep, |
| int fsfd, |
| xfs_bstat_t *statp, |
| char *namep ), |
| void *arg1, |
| int *cbrvalp, |
| char *usrgdp, |
| size_t usrgdsz ) |
| { |
| size_t gdsz; |
| struct dirent *gdp; |
| int fd; |
| int gdcnt; |
| int scrval; |
| int cbrval; |
| |
| if ( usrgdp ) { |
| assert( usrgdsz >= sizeof( struct dirent ) ); |
| gdsz = usrgdsz; |
| gdp = ( struct dirent * )usrgdp; |
| } else { |
| gdsz = pgsz; |
| gdp = ( struct dirent * )malloc( gdsz ); |
| assert( gdp ); |
| } |
| |
| /* open the directory |
| */ |
| fd = jdm_open( fshandlep, statp, O_RDONLY ); |
| if ( fd < 0 ) { |
| mlog( MLOG_NORMAL, |
| _("WARNING: unable to open directory ino %llu: %s\n"), |
| statp->bs_ino, |
| strerror( errno )); |
| *cbrvalp = 0; |
| if ( ! usrgdp ) { |
| free( ( void * )gdp ); |
| } |
| return -1; |
| } |
| assert( ( statp->bs_mode & S_IFMT ) == S_IFDIR ); |
| |
| /* lots of buffering done here, to achieve OS-independence. |
| * if proves to be to much overhead, can streamline. |
| */ |
| scrval = 0; |
| cbrval = 0; |
| for ( gdcnt = 1 ; ; gdcnt++ ) { |
| struct dirent *p; |
| int nread; |
| register size_t reclen; |
| |
| assert( scrval == 0 ); |
| assert( cbrval == 0 ); |
| |
| nread = getdents_wrap( fd, (char *)gdp, gdsz ); |
| |
| /* negative count indicates something very bad happened; |
| * try to gracefully end this dir. |
| */ |
| if ( nread < 0 ) { |
| mlog( MLOG_NORMAL, |
| _("WARNING: unable to read dirents (%d) for " |
| "directory ino %llu: %s\n"), |
| gdcnt, |
| statp->bs_ino, |
| strerror( errno )); |
| nread = 0; /* pretend we are done */ |
| } |
| |
| /* no more directory entries: break; |
| */ |
| if ( nread == 0 ) { |
| break; |
| } |
| |
| /* translate and invoke cb each entry: skip "." and "..". |
| */ |
| for ( p = gdp, |
| reclen = ( size_t )p->d_reclen |
| ; |
| nread > 0 |
| ; |
| nread -= ( int )reclen, |
| assert( nread >= 0 ), |
| p = ( struct dirent * )( ( char * )p + reclen ), |
| reclen = ( size_t )p->d_reclen ) { |
| xfs_bstat_t statbuf; |
| assert( scrval == 0 ); |
| assert( cbrval == 0 ); |
| |
| /* skip "." and ".." |
| */ |
| if ( *( p->d_name + 0 ) == '.' |
| && |
| ( *( p->d_name + 1 ) == 0 |
| || |
| ( *( p->d_name + 1 ) == '.' |
| && |
| *( p->d_name + 2 ) == 0 ))) { |
| continue; |
| } |
| |
| /* use bigstat |
| */ |
| scrval = bigstat_one( fsfd, |
| p->d_ino, |
| &statbuf ); |
| if ( scrval ) { |
| mlog( MLOG_NORMAL, |
| _("WARNING: could not stat " |
| "dirent %s ino %llu: %s\n"), |
| p->d_name, |
| ( xfs_ino_t )p->d_ino, |
| strerror( errno )); |
| scrval = 0; |
| continue; |
| } |
| |
| /* if getdents64() not yet available, and ino |
| * occupied more than 32 bits, warn and skip. |
| */ |
| #ifndef __USE_LARGEFILE64 |
| if ( statbuf.bs_ino > ( xfs_ino_t )INOMAX ) { |
| mlog( MLOG_NORMAL, |
| _("WARNING: unable to process dirent %s: " |
| "ino %llu too large\n"), |
| p->d_name, |
| ( xfs_ino_t )p->d_ino ); |
| continue; |
| } |
| #endif |
| |
| /* invoke the callback |
| */ |
| cbrval = ( * cbfp )( arg1, |
| fshandlep, |
| fsfd, |
| &statbuf, |
| p->d_name ); |
| |
| /* abort the iteration if the callback returns non-zero |
| */ |
| if ( cbrval ) { |
| break; |
| } |
| } |
| |
| if ( scrval || cbrval ) { |
| break; |
| } |
| } |
| |
| ( void )close( fd ); |
| if ( ! usrgdp ) { |
| free( ( void * )gdp ); |
| } |
| |
| if ( scrval ) { |
| *cbrvalp = 0; |
| return -1; |
| } else if ( cbrval ) { |
| *cbrvalp = cbrval; |
| return 1; |
| } else { |
| *cbrvalp = 0; |
| return 0; |
| } |
| } |
| |
| int |
| cvtnum( int blocksize, char *s ) |
| { |
| int i; |
| char *sp; |
| |
| i = (int)strtoll(s, &sp, 0); |
| if (i == 0 && sp == s) |
| return -1; |
| if (*sp == '\0') |
| return i; |
| if (*sp == 'b' && blocksize && sp[1] == '\0') |
| return i * blocksize; |
| if (*sp == 'k' && sp[1] == '\0') |
| return 1024 * i; |
| if (*sp == 'm' && sp[1] == '\0') |
| return 1024 * 1024 * i; |
| return -1; |
| } |