示例#1
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Create a file node
 *
 * If there is no create() operation, mknod() will be called for
 * creation of all non-directory, non-symlink nodes.
 */
int bb_mknod(const char *path, mode_t mode, dev_t dev) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_mknod(path=\"%s\", mode=0%3o, dev=%lld)\n", path, mode, dev);
   bb_fullpath(fpath, path);
    
   // On Linux this could just be 'mknod(path, mode, rdev)' but this
   //  is more portable
   if (S_ISREG(mode)) {
      retstat = open(fpath, O_CREAT | O_EXCL | O_WRONLY, mode);
      if (retstat < 0) retstat = bb_error("bb_mknod open");
      else {
	 retstat = close(retstat);
	 if (retstat < 0) retstat = bb_error("bb_mknod close");
      }
   } else if (S_ISFIFO(mode)) {
      retstat = mkfifo(fpath, mode);
      if (retstat < 0) retstat = bb_error("bb_mknod mkfifo");
   } else {
      retstat = mknod(fpath, mode, dev);
      if (retstat < 0) retstat = bb_error("bb_mknod mknod");
   }
   return retstat;
}
示例#2
0
void bb_wq_callback(struct ev_loop *loop, struct ev_io *w, int revents) {
	struct bb_writer *bbw = (struct bb_writer *) w;
	struct bb_session *bbs = bbw->session;
	struct bb_writer_item *consumed_bbwi,*bbwi = bbw->head;
	while(bbwi) {
		if (bbwi->close_it) goto end;
		if (bbwi->len == 0) goto next;
		ssize_t wlen = write(bbs->fd, bbwi->buf+bbwi->pos, bbwi->len-bbwi->pos);
		if (wlen < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK) {
				return ;
			}
			bb_error("unable to write to client: write()");
			goto end;
		}
		if (wlen == 0) {
			bb_error("client disconnected: write()");
			goto end;
		}
		if (wlen < bbwi->len-bbwi->pos) {
			bbwi->pos-=wlen;
			return;
		}
next:
		consumed_bbwi = bbwi;
		bbwi = bbwi->next;
		// remove the consumed item
		wq_decapitate(bbw);
	}
	ev_io_stop(blastbeat.loop, w);
	return;
end:
	bb_session_close(bbs);
}
示例#3
0
int bb_socketio_push(struct bb_session *bbs, char type, char *buf, size_t len) {

    char *message = bb_alloc(4 + len);
    if (!message) {
        bb_error("malloc()");
        return -1;
    }

    message[0] = type;
    message[1] = ':';
    message[2] = ':';
    message[3] = ':';

    memcpy(message+4, buf, len);

    if (bbs->sio_realtime) {
        return bb_websocket_reply(bbs, message, len+4);
    }


    struct bb_socketio_message *last_bbsm=NULL,*bbsm = bbs->sio_queue;

    while(bbsm) {
        last_bbsm = bbsm;
        bbsm = bbsm->next;
    }

    bbsm = bb_alloc(sizeof(struct bb_socketio_message));
    if (!bbsm) {
        bb_free(message, len+4);
        bb_error("malloc()");
        return -1;
    }
    memset(bbsm, 0, sizeof(struct bb_socketio_message));
    bbsm->buf = message;
    bbsm->len = 4+len;
    if (last_bbsm) {
        last_bbsm->next = bbsm;
    }
    else {
        bbs->sio_queue = bbsm;
    }

    //is a poller attached to the session ?
    if (bbs->sio_poller) {
        ev_feed_event(blastbeat.loop, &bbs->death_timer, EV_TIMER);
    }

    return 0;
}
示例#4
0
/*

	The BlastBeat write queue

	Non-blocking writes are pretty complex to manage
	This implementation uses a queue of buffers associated to an ev_io structure

	Whenever an item is put in the queue an ev_io writer will be start and it will try to
	write datas until it fails (returning EAGAIN or an incomplete write)

	The offset in each item is required for managing incomplete writes

*/
static int wq_push(struct bb_writer *bbw, char *buf, size_t len, int free_it, int close_it) {
	
	struct bb_writer_item *bbwi = malloc(sizeof(struct bb_writer_item));
	if (!bbwi) {
		bb_error("unable to allocate memory for a writequeue item: malloc()");
		return -1;
	}

	bbwi->buf = buf;
	bbwi->pos = 0;
	bbwi->len = len;
	bbwi->free_it = free_it;
	bbwi->close_it = close_it;
	bbwi->next = NULL;
	
	if (!bbw->head) {
		bbw->head = bbwi;
		bbw->tail = bbwi;
	}
	else {
		bbw->tail->next = bbwi;
		bbwi->prev = bbw->tail;
	}

	bbw->tail = bbwi;
	return 0;
}
示例#5
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Read data from an open file
 *
 * Read should return exactly the number of bytes requested except
 * on EOF or error, otherwise the rest of the data will be
 * substituted with zeroes.  An exception to this is when the
 * 'direct_io' mount option is specified, in which case the return
 * value of the read system call will reflect the return value of
 * this operation.
 *
 */
int bb_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
   int retstat = 0; int count; int count2;
    
   log_msg("\nbb_read(path=\"%s\", buf=0x%08x, size=%d, offset=%lld, fi=0x%08x)\n", path, buf, size, offset, fi);
   // no need to get fpath on this one, since I work from fi->fh not the path
   log_fi(fi);

   /*
    * The pread() function attempts to read the specified amount (size) of 
    * data from the specified file descriptor (fi->fh), into the specified 
    * buffer (buf) starting at a point in the file (offset).  Returns the
    * number of bytes read.
    */
   retstat = pread(fi->fh, buf, size, offset);
   
	log_msg("\nFile \"%s\" has been encrypted\n", path);
	// Encrypts the chars before reading it
	for (count = 0; count < size; count++) {
		buf[count] = (buf[count]+1) % 256;
   }
	// Decrypts if HasAccess is true
   if (HasAccess(BB_DATA)) {
      for (count2 = 0; count2 < size; count2++) {
   		buf[count2] = (buf[count2] == 0) ? 255 : ((buf[count2]-1) % 256);
		}
		log_msg("\nAuthorized decryption on file \"%s\"\n", path);
   }
   
   if (retstat < 0) retstat = bb_error("bb_read read");
   
   return retstat;
}
示例#6
0
文件: main.c 项目: 20tab/blastbeat
static struct bb_virtualhost *bb_vhost_map_to_acceptor(struct bb_acceptor *acceptor, struct bb_virtualhost *vhost) {
	struct bb_virtualhost *v = bb_get_vhost(acceptor->vhosts, vhost->name, vhost->len);
	if (v) return v;

	v = malloc(sizeof(struct bb_virtualhost));
	if (!v) {
		bb_error("malloc()");
		return NULL;
	}
	memcpy(v, vhost, sizeof(struct bb_virtualhost));
	// fix the new object
	v->next = NULL;

	if (!acceptor->vhosts) {
		acceptor->vhosts = v;
		return v;
	}
	else {
		struct bb_virtualhost *vhosts = acceptor->vhosts;
		while(vhosts) {
			if (!vhosts->next) {
				vhosts->next = v;
				return v;
			}
			vhosts = vhosts->next;
		}
	}

	return NULL;
}
示例#7
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Read directory
 * 
 * Uses function called 'filler' to insert directory entries into the 
 * directory structure, which is also passed to the callback as 'buf'.
 *
 * This supersedes the old getdir() interface.  New applications
 * should use this.
 *
 * The filesystem may choose between two modes of operation:
 *
 * 1) The readdir implementation ignores the offset parameter, and
 * passes zero to the filler function's offset.  The filler
 * function will not return '1' (unless an error happens), so the
 * whole directory is read in a single readdir operation.  This
 * works just like the old getdir() method.
 *
 * 2) The readdir implementation keeps track of the offsets of the
 * directory entries.  It uses the offset parameter and always
 * passes non-zero offset to the filler function.  When the buffer
 * is full (or an error happens) the filler function will return
 * '1'.
 *
 */
int bb_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
   int retstat = 0;
   DIR *dp;
   struct dirent *de;
    
   log_msg("\nbb_readdir(path=\"%s\", buf=0x%08x, filler=0x%08x, offset=%lld, fi=0x%08x)\n", path, buf, filler, offset, fi);
   // once again, no need for fullpath -- but note that I need to cast fi->fh
   dp = (DIR*) (uintptr_t) fi->fh;

   // Every directory contains at least two entries: . and ..  If my
   // first call to the system readdir() returns NULL I've got an
   // error; near as I can tell, that's the only condition under
   // which I can get an error from readdir()
   de = readdir(dp);
   if (de == 0) {
      retstat = bb_error("bb_readdir readdir");
      return retstat;
   }

   // This will copy the entire directory into the buffer.  The loop exits
   // when either the system readdir() returns NULL, or filler()
   // returns something non-zero.  The first case just means I've
   // read the whole directory; the second means the buffer is full.
   do {
      log_msg("calling filler with name %s\n", de->d_name);
      if (filler(buf, de->d_name, NULL, 0) != 0) {
	 log_msg("    ERROR bb_readdir filler:  buffer full");
	 return -ENOMEM;
      }
   } while ((de = readdir(dp)) != NULL);
    
   log_fi(fi);
   return retstat;
}
示例#8
0
文件: main.c 项目: 20tab/blastbeat
int bb_nonblock(int fd) {
	int arg;

        arg = fcntl(fd, F_GETFL, NULL);
        if (arg < 0) {
                bb_error("fcntl()");
		return -1;
        }
        arg |= O_NONBLOCK;
        if (fcntl(fd, F_SETFL, arg) < 0) {
                bb_error("fcntl()");
                return -1;
        }

	return 0;
}
示例#9
0
文件: main.c 项目: 20tab/blastbeat
struct bb_session *bb_session_new(struct bb_connection *bbc) {
	struct bb_session *bbs = malloc(sizeof(struct bb_session));
	if (!bbs) {
		bb_error("malloc()");
		return NULL;
	}
	memset(bbs, 0, sizeof(struct bb_session));
	// put the session in the hashtable
	bb_sht_add(bbs);
	// prepare for allocating a new request
	bbs->new_request = 1;
	// link to the connection
	bbs->connection = bbc;
	if (!bbc->sessions_head) {
		bbc->sessions_head = bbs;
		bbc->sessions_tail = bbs;
	}
	else {
		bbs->conn_prev = bbc->sessions_tail;
		bbc->sessions_tail = bbs;
		bbs->conn_prev->next = bbs;
	}

	return bbs;
}
示例#10
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** File open operation
 *
 * No creation, or truncation flags (O_CREAT, O_EXCL, O_TRUNC)
 * will be passed to open().  Open should check if the operation
 * is permitted for the given flags.  Optionally open may also
 * return an arbitrary filehandle in the fuse_file_info structure,
 * which will be passed to all file operations.
 *
 * path = path to file, relative to rootdir
 * fi = contains file information for the file to be opened
 * struct fuse_file_info defined in Rootdir/fuse-2.9.2/include/fuse_common.h
 */
int bb_open(const char *path, struct fuse_file_info *fi) {
   int retstat = 0;
   int fd;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_open(path\"%s\", fi=0x%08x)\n", path, fi);
   bb_fullpath(fpath, path);  // path is relative to rootdir, must change it
                              // to a full path
    
   fd = open(fpath, fi->flags);  // uses system open to open or return an error
                                 // returns a file despcriptor (> 0) on success
   if (fd < 0) retstat = bb_error("bb_open open");
    
   fi->fh = fd;  // sets the handle field of fi
   log_fi(fi);   
   return retstat;  // This should be 0 - but we would like to return a file
                    // descriptor.  Fuse automatically chooses a file descriptor
                    // and returns that one (may be different from open's ret)
                    // fi->fh is different from descriptor returned as retstat
                    // Calls to read, say, include fi, hence fi->fh
                    // But programs think they are talking to retstat
                    // When program does a read, the call is intercepted by
                    // the kernel and redirected to a bb_read where fi->fh
                    // is used as the handle
}
示例#11
0
文件: main.c 项目: 20tab/blastbeat
void bb_raw_zmq_send_msg(char *identity, size_t identity_len, char *sid, size_t sid_len, char *t, size_t t_len, char *body, size_t body_len) {

	zmq_msg_t z_i,z_sid,z_t, z_body;

        zmq_msg_init_size(&z_i, identity_len);
        zmq_msg_init_size(&z_sid, sid_len);
        zmq_msg_init_size(&z_t, t_len);
        zmq_msg_init_size(&z_body, body_len);

        memcpy(zmq_msg_data(&z_i), identity, identity_len);
        memcpy(zmq_msg_data(&z_sid), sid, sid_len);
        memcpy(zmq_msg_data(&z_t), t, t_len);
        memcpy(zmq_msg_data(&z_body), body, body_len);


        zmq_send(blastbeat.router, &z_i, ZMQ_SNDMORE);
        zmq_send(blastbeat.router, &z_sid, ZMQ_SNDMORE);
        zmq_send(blastbeat.router, &z_t, ZMQ_SNDMORE);
	for(;;) {
        	int ret = zmq_send(blastbeat.router, &z_body, ZMQ_NOBLOCK);
		if (!ret) break;
		if (errno == EAGAIN) continue;
                bb_error("zmq_send()");
		break;
        }

	zmq_msg_close(&z_i);
	zmq_msg_close(&z_sid);
	zmq_msg_close(&z_t);
	zmq_msg_close(&z_body);

}
示例#12
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Remove extended attributes */
int bb_removexattr(const char *path, const char *name) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_removexattr(path=\"%s\", name=\"%s\")\n", path, name);
   bb_fullpath(fpath, path);
    
   retstat = lremovexattr(fpath, name);
   if (retstat < 0) retstat = bb_error("bb_removexattr lrmovexattr");
   return retstat;
}
示例#13
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Create a directory */
int bb_mkdir(const char *path, mode_t mode) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_mkdir(path=\"%s\", mode=0%3o)\n", path, mode);
   bb_fullpath(fpath, path);
    
   retstat = mkdir(fpath, mode);
   if (retstat < 0) retstat = bb_error("bb_mkdir mkdir");
   return retstat;
}
示例#14
0
static int bb_socketio_recv_body(struct bb_session *bbs, char *buf, size_t len) {
    char *new_buf = bb_realloc(bbs->request.sio_post_buf, bbs->request.sio_post_buf_size, len);
    if (!new_buf) {
        bb_error("realloc()");
        return -1;
    }
    bbs->request.sio_post_buf = new_buf;
    memcpy(bbs->request.sio_post_buf+bbs->request.sio_post_buf_size, buf, len);
    bbs->request.sio_post_buf_size+=len;
    return 0;
}
示例#15
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/* note -- I'll want to change this as soon as 2.6 is in debian testing */
int bb_utime(const char *path, struct utimbuf *ubuf) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_utime(path=\"%s\", ubuf=0x%08x)\n", path, ubuf);
   bb_fullpath(fpath, path);
   
   retstat = utime(fpath, ubuf);
   if (retstat < 0) retstat = bb_error("bb_utime utime");
   return retstat;
}
示例#16
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Change the size of a file */
int bb_truncate(const char *path, off_t newsize) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_truncate(path=\"%s\", newsize=%lld)\n", path, newsize);
   bb_fullpath(fpath, path);
    
   retstat = truncate(fpath, newsize);
   if (retstat < 0) bb_error("bb_truncate truncate");
   return retstat;
}
示例#17
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Change the owner and group of a file */
int bb_chown(const char *path, uid_t uid, gid_t gid) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_chown(path=\"%s\", uid=%d, gid=%d)\n", path, uid, gid);
   bb_fullpath(fpath, path);
    
   retstat = chown(fpath, uid, gid);
   if (retstat < 0) retstat = bb_error("bb_chown chown");
   return retstat;
}
示例#18
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Change the permission bits of a file */
int bb_chmod(const char *path, mode_t mode) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_chmod(fpath=\"%s\", mode=0%03o)\n", path, mode);
   bb_fullpath(fpath, path);
    
   retstat = chmod(fpath, mode);
   if (retstat < 0) retstat = bb_error("bb_chmod chmod");
   return retstat;
}
示例#19
0
文件: bbfs.c 项目: blakfishy/OPRSYS
// The parameters here are a little bit confusing, but do correspond
// to the symlink() system call.  The 'path' is where the link points,
// while the 'link' is the link itself.  So we need to leave the path
// unaltered, but insert the link into the mounted directory.
int bb_symlink(const char *path, const char *link) {
   int retstat = 0;
   char flink[PATH_MAX];
    
   log_msg("\nbb_symlink(path=\"%s\", link=\"%s\")\n", path, link);
   bb_fullpath(flink, link);
    
   retstat = symlink(path, flink);
   if (retstat < 0) retstat = bb_error("bb_symlink symlink");
   return retstat;
}
示例#20
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Remove a directory */
int bb_rmdir(const char *path) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("bb_rmdir(path=\"%s\")\n", path);
   bb_fullpath(fpath, path);
    
   retstat = rmdir(fpath);
    if (retstat < 0) retstat = bb_error("bb_rmdir rmdir");
    return retstat;
}
示例#21
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Remove a file */
int bb_unlink(const char *path) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("bb_unlink(path=\"%s\")\n", path);
   bb_fullpath(fpath, path);
    
   retstat = unlink(fpath);
   if (retstat < 0) retstat = bb_error("bb_unlink unlink");
   return retstat;
}
示例#22
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/**
 * Check file access permissions
 *
 * This will be called for the access() system call.  If the
 * 'default_permissions' mount option is given, this method is not
 * called.
 *
 * This method is not called under Linux kernel versions 2.4.x
 *
 * Introduced in version 2.5
 */
int bb_access(const char *path, int mask) {
   int retstat = 0;
   char fpath[PATH_MAX];
   
   log_msg("\nbb_access(path=\"%s\", mask=0%o)\n", path, mask);
   bb_fullpath(fpath, path);
    
   retstat = access(fpath, mask);
   if (retstat < 0) retstat = bb_error("bb_access access");
   return retstat;
}
示例#23
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Set extended attributes */
int bb_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_setxattr(path=\"%s\", name=\"%s\", value=\"%s\", size=%d, flags=0x%08x)\n", path, name, value, size, flags);
   bb_fullpath(fpath, path);
    
   retstat = lsetxattr(fpath, name, value, size, flags);
   if (retstat < 0) retstat = bb_error("bb_setxattr lsetxattr");
   return retstat;
}
示例#24
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/**
 * Change the size of an open file
 *
 * This method is called instead of the truncate() method if the
 * truncation was invoked from an ftruncate() system call.
 *
 * If this method is not implemented or under Linux kernel
 * versions earlier than 2.6.15, the truncate() method will be
 * called instead.
 *
 * Introduced in version 2.5
 */
int bb_ftruncate(const char *path, off_t offset, struct fuse_file_info *fi) {
   int retstat = 0;
   
   log_msg("\nbb_ftruncate(path=\"%s\", offset=%lld, fi=0x%08x)\n", 
	   path, offset, fi);
   log_fi(fi);
    
   retstat = ftruncate(fi->fh, offset);
   if (retstat < 0) retstat = bb_error("bb_ftruncate ftruncate");
   return retstat;
}
示例#25
0
int bb_removexattr(const char *path, const char *name)
{
    int retstat = 0;

    log_msg("\nbb_removexattr(path=\"%s\", name=\"%s\")\n", path, name);

    if (retstat < 0)
	retstat = bb_error("bb_removexattr lrmovexattr");

    return retstat;
}
示例#26
0
文件: bbfs.c 项目: blakfishy/OPRSYS
// As  with read(), the documentation above is inconsistent with the
// documentation for the write() system call.
int bb_write(const char *path, const char *buf, size_t size, off_t offset,
	     struct fuse_file_info *fi) {
   int retstat = 0;
    
   log_msg("\nbb_write(path=\"%s\", buf=0x%08x, size=%d, offset=%lld, fi=0x%08x)\n", path, buf, size, offset, fi);
   // no need to get fpath on this one, since I work from fi->fh not the path
   log_fi(fi);
	
   retstat = pwrite(fi->fh, buf, size, offset);
   if (retstat < 0) retstat = bb_error("bb_write pwrite");
   return retstat;
}
示例#27
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Synchronize file contents
 *
 * If the datasync parameter is non-zero, then only the user data
 * should be flushed, not the meta data.
 *
 * Changed in version 2.2
 */
int bb_fsync(const char *path, int datasync, struct fuse_file_info *fi) {
   int retstat = 0;
    
   log_msg("\nbb_fsync(path=\"%s\", datasync=%d, fi=0x%08x)\n",path,datasync,fi);
   log_fi(fi);
    
   if (datasync) retstat = fdatasync(fi->fh);
   else	retstat = fsync(fi->fh);
    
   if (retstat < 0) bb_error("bb_fsync fsync");
   return retstat;
}
示例#28
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Get extended attributes */
int bb_getxattr(const char *path, const char *name, char *value, size_t size) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_getxattr(path = \"%s\", name = \"%s\", value = 0x%08x, size = %d)\n", path, name, value, size);
   bb_fullpath(fpath, path);
    
   retstat = lgetxattr(fpath, name, value, size);
   if (retstat < 0) retstat = bb_error("bb_getxattr lgetxattr");
   else log_msg("    value = \"%s\"\n", value);
   return retstat;
}
示例#29
0
文件: bbfs.c 项目: blakfishy/OPRSYS
// Since it's currently only called after bb_create(), and bb_create()
// opens the file, I ought to be able to just use the fd and ignore
// the path...
int bb_fgetattr(const char *path, struct stat *statbuf, struct fuse_file_info *fi) {
   int retstat = 0;
    
   log_msg("\nbb_fgetattr(path=\"%s\", statbuf=0x%08x, fi=0x%08x)\n",
	   path, statbuf, fi);
   log_fi(fi);
    
   retstat = fstat(fi->fh, statbuf);
   if (retstat < 0) retstat = bb_error("bb_fgetattr fstat");
   log_stat(statbuf);
   return retstat;
}
示例#30
0
文件: bbfs.c 项目: blakfishy/OPRSYS
/** Get file attributes.
 *
 * Similar to stat().  The 'st_dev' and 'st_blksize' fields are
 * ignored.  The 'st_ino' field is ignored except if the 'use_ino'
 * mount option is given.
 */
int bb_getattr(const char *path, struct stat *statbuf) {
   int retstat = 0;
   char fpath[PATH_MAX];
    
   log_msg("\nbb_getattr(path=\"%s\", statbuf=0x%08x)\n", path, statbuf);
   bb_fullpath(fpath, path);
   
   retstat = lstat(fpath, statbuf);
   if (retstat != 0) retstat = bb_error("bb_getattr lstat");
   log_stat(statbuf);
   return retstat;
}