static void skipbytes(struct context *ctx, int cur __attribute__ ((unused))) { off_t ro = ctx->io_offset, off = 0; DebugIn(DEBUG_BUFFER); sigbus_cur = ctx->cfn; if (chunk_get(ctx, NULL)) { io_sched_pop(ctx->io, ctx); ctx->dbufi = buffer_free_all(ctx->dbufi); ctx->remaining = 0, ctx->offset = 0; cleanup_file(ctx, ctx->ffn); cleanup_data(ctx, ctx->ffn); reply(ctx, MSG_451_Internal_error); } else { if (chunk_remaining(ctx)) { char *u = ctx->chunk_start; char lastchar = ctx->lastchar; size_t len = MIN(ctx->chunk_length, (size_t) bufsize); char *ul = u + len; for (off = 0; ro && u < ul; ro--, off++, lastchar = *u++) if (*u == '\n' && lastchar != '\r') ro--; ctx->lastchar = lastchar; chunk_release(ctx, len); } if (!chunk_remaining(ctx)) ro = 0; if (!ro) { ctx->dbufi = buffer_free_all(ctx->dbufi); lseek(ctx->ffn, ctx->offset + off, SEEK_SET); ctx->remaining = 0, ctx->offset = 0; if (io_get_cb_i(ctx->io, ctx->dfn) == (void *) socket2buffer) { /* already connected */ io_clr_o(ctx->io, ctx->dfn); io_set_i(ctx->io, ctx->dfn); } io_sched_pop(ctx->io, ctx); } else io_sched_renew_proc(ctx->io, ctx, (void *) skipbytes); ctx->io_offset = ro; } DebugOut(DEBUG_BUFFER); }
static void catchbus(int sig __attribute__ ((unused))) { struct context *ctx; if (sigbus_cur > -1 && (ctx = io_get_ctx(ctx_spawnd->io, sigbus_cur))) { logmsg("catched SIGBUS (%s)", ctx->filename); ctx->dbuf = buffer_free_all(ctx->dbuf); ctx->dbufi = buffer_free_all(ctx->dbufi); ctx->chunk_start = NULL; ctx->chunk_length = 0; ctx->remaining = 0; cleanup_file(ctx, sigbus_cur); if (ctx->dfn > -1) { reply(ctx, MSG_451_Transfer_incomplete); cleanup_data(ctx, ctx->dfn); } } signal(SIGBUS, catchbus); longjmp(sigbus_jmpbuf, 1); }
iface_t *init_file (iface_t *ifa) { struct if_file *ifc; struct kopts *opt; struct stat statbuf; int append=0; if ((ifc = (struct if_file *)malloc(sizeof(struct if_file))) == NULL) { logerr(errno,"Could not allocate memory"); return(NULL); } memset ((void *)ifc,0,sizeof(struct if_file)); ifc->qsize=DEFFILEQSIZE; ifa->info = (void *) ifc; for(opt=ifa->options;opt;opt=opt->next) { if (!strcasecmp(opt->var,"filename")) { if (strcmp(opt->val,"-")) if ((ifc->filename=strdup(opt->val)) == NULL) { logerr(errno,"Failed to duplicate argument string"); return(NULL); } } else if (!strcasecmp(opt->var,"qsize")) { if (!(ifc->qsize=atoi(opt->val))) { logerr(0,"Invalid queue size specified: %s",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"append")) { if (!strcasecmp(opt->val,"yes")) { append++; } else if (!strcasecmp(opt->val,"no")) { append = 0; } else { logerr(0,"Invalid option \"append=%s\"",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"eol")) { if (!strcasecmp(opt->val,"rn")) ifc->usereturn=1; else if (!strcasecmp(opt->val,"n")) { ifc->usereturn=0; } else { logerr(0,"Invalid option \"eol=%s\": Must be \"n\" or \"rn\"", opt->val); return(NULL); } } else { logerr(0,"Unknown interface option %s\n",opt->var); return(NULL); } } /* We do allow use of stdin and stdout, but not if they're connected to * a terminal. This allows re-direction in background mode */ if (ifc->filename == NULL) { if (ifa->persist) { logerr(0,"Can't use persist mode with stdin/stdout"); return(NULL); } if (((ifa->direction != IN) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDOUT)) || ((ifa->direction != OUT) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDIN))) { logerr(0,"Can't use terminal stdin/stdout in background mode"); return(NULL); } ifc->fp = (ifa->direction == IN)?stdin:stdout; } else { if (ifa->direction == BOTH) { logerr(0,"Bi-directional file I/O only supported for stdin/stdout"); return(NULL); } if (stat(ifc->filename,&statbuf) < 0) { if (ifa->direction != OUT) { logerr(errno,"stat %s",ifc->filename); return(NULL); } } if (S_ISFIFO(statbuf.st_mode)) { /* Special rules for FIFOs. Opening here would hang for a reading * interface with no writer. Given that we're single threaded here, * that would be bad */ if (access(ifc->filename,(ifa->direction==IN)?R_OK:W_OK) != 0) { logerr(errno,"Could not access %s",ifc->filename); return(NULL); } } else { if (ifa->persist) { logerr(0,"Can't use persist mode on %s: Not a FIFO", ifc->filename); return(NULL); } if ((ifc->fp=fopen(ifc->filename,(ifa->direction==IN)?"r": (append)?"a":"w")) == NULL) { logerr(errno,"Failed to open %s",ifc->filename); return(NULL); } if (ifa->direction == OUT) /* Make output line buffered */ setlinebuf(ifc->fp); } } free_options(ifa->options); ifa->write=write_file; ifa->read=read_file; ifa->cleanup=cleanup_file; if (ifa->direction != IN && ifc->fp != NULL) if ((ifa->q =init_q(ifc->qsize)) == NULL) { logerr(0,"Could not create queue"); cleanup_file(ifa); return(NULL); } if (ifa->direction == BOTH) { if ((ifa->next=ifdup(ifa)) == NULL) { logerr(0,"Interface duplication failed"); cleanup_file(ifa); return(NULL); } ifa->direction=OUT; ifa->pair->direction=IN; ifc = (struct if_file *) ifa->pair->info; ifc->fp=stdin; } return(ifa); }
iface_t *init_file (iface_t *ifa) { struct if_file *ifc; struct kopts *opt; struct stat statbuf; int ret; int append=0; uid_t uid=-1; gid_t gid=-1; struct passwd *owner; struct group *group; mode_t tperm,perm=0; char *cp; if ((ifc = (struct if_file *)malloc(sizeof(struct if_file))) == NULL) { logerr(errno,"Could not allocate memory"); return(NULL); } memset ((void *)ifc,0,sizeof(struct if_file)); ifc->qsize=DEFFILEQSIZE; ifc->fd=-1; ifa->info = (void *) ifc; for(opt=ifa->options;opt;opt=opt->next) { if (!strcasecmp(opt->var,"filename")) { if (strcmp(opt->val,"-")) if ((ifc->filename=strdup(opt->val)) == NULL) { logerr(errno,"Failed to duplicate argument string"); return(NULL); } } else if (!strcasecmp(opt->var,"qsize")) { if (!(ifc->qsize=atoi(opt->val))) { logerr(0,"Invalid queue size specified: %s",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"append")) { if (!strcasecmp(opt->val,"yes")) { append++; } else if (!strcasecmp(opt->val,"no")) { append = 0; } else { logerr(0,"Invalid option \"append=%s\"",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"owner")) { if ((owner=getpwnam(opt->val)) == NULL) { logerr(0,"No such user '%s'",opt->val); return(NULL); } uid=owner->pw_uid; } else if (!strcasecmp(opt->var,"group")) { if ((group=getgrnam(opt->val)) == NULL) { logerr(0,"No such group '%s'",opt->val); return(NULL); } gid=group->gr_gid; } else if (!strcasecmp(opt->var,"perm")) { for (cp=opt->val;*cp;cp++) { if (*cp >= '0' && *cp < '8') { perm <<=3; perm += (*cp-'0'); } else { perm = 0; break; } } perm &= ACCESSPERMS; if (perm == 0) { logerr(0,"Invalid permissions for tty device \'%s\'",opt->val); return 0; } } else { logerr(0,"Unknown interface option %s\n",opt->var); return(NULL); } } /* We do allow use of stdin and stdout, but not if they're connected to * a terminal. This allows re-direction in background mode */ if (ifc->filename == NULL) { if (flag_test(ifa,F_PERSIST)) { logerr(0,"Can't use persist mode with stdin/stdout"); return(NULL); } if (((ifa->direction != IN) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDOUT)) || ((ifa->direction != OUT) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDIN))) { logerr(0,"Can't use terminal stdin/stdout in background mode"); return(NULL); } ifc->fd = (ifa->direction == IN)?STDIN_FILENO:STDOUT_FILENO; } else { if (ifa->direction == BOTH) { logerr(0,"Bi-directional file I/O only supported for stdin/stdout"); return(NULL); } if ((ret=stat(ifc->filename,&statbuf)) < 0) { if (ifa->direction != OUT) { logerr(errno,"stat %s",ifc->filename); return(NULL); } } if ((ret == 0) && S_ISFIFO(statbuf.st_mode)) { /* Special rules for FIFOs. Opening here would hang for a reading * interface with no writer. Given that we're single threaded here, * that would be bad */ if (access(ifc->filename,(ifa->direction==IN)?R_OK:W_OK) != 0) { logerr(errno,"Could not access %s",ifc->filename); return(NULL); } } else { if (flag_test(ifa,F_PERSIST)) { logerr(0,"Can't use persist mode on %s: Not a FIFO", ifc->filename); return(NULL); } if (perm) tperm=umask(0); errno=0; if (ifa->direction != IN && (ifc->fd=open(ifc->filename, O_WRONLY|O_CREAT|O_EXCL|((append)?O_APPEND:0), (perm)?perm:0664)) >= 0) { if (gid != 0 || uid != -1) { if (chown(ifc->filename,uid,gid) < 0) { logerr(errno, "Failed to set ownership or group on output file %s",ifc->filename); return(NULL); } } } else { if (errno && errno != EEXIST) { logerr(errno,"Failed to create file %s",ifc->filename); return(NULL); } if ((ifc->fd=open(ifc->filename,(ifa->direction==IN)?O_RDONLY: (O_WRONLY|((append)?O_APPEND:O_TRUNC)))) < 0) { logerr(errno,"Failed to open file %s",ifc->filename); return(NULL); } } /* reset umask: not really necessary */ if (perm) (void) umask(tperm); } } free_options(ifa->options); ifa->write=write_file; ifa->read=file_read_wrapper; ifa->readbuf=read_file; ifa->cleanup=cleanup_file; if (ifa->direction != IN && ifc->fd >= 0) if ((ifa->q =init_q(ifc->qsize)) == NULL) { logerr(0,"Could not create queue"); cleanup_file(ifa); return(NULL); } if (ifa->direction == BOTH) { if ((ifa->next=ifdup(ifa)) == NULL) { logerr(0,"Interface duplication failed"); cleanup_file(ifa); return(NULL); } ifa->direction=OUT; ifa->pair->direction=IN; ifc = (struct if_file *) ifa->pair->info; ifc->fd=STDIN_FILENO; } return(ifa); }
/* * Helper function for unionfs_file_revalidate/locked. * Expects dentry/parent to be locked already, and revalidated. */ static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry, struct dentry *parent, struct super_block *sb, int sbgen, int dgen, bool willwrite) { int fgen; int bstart, bend, orig_brid; int size; int err = 0; fgen = atomic_read(&UNIONFS_F(file)->generation); /* * There are two cases we are interested in. The first is if the * generation is lower than the super-block. The second is if * someone has copied up this file from underneath us, we also need * to refresh things. */ if (d_deleted(dentry) || (sbgen <= fgen && dbstart(dentry) == fbstart(file) && unionfs_lower_file(file))) goto out_may_copyup; /* save orig branch ID */ orig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; /* First we throw out the existing files. */ cleanup_file(file); /* Now we reopen the file(s) as in unionfs_open. */ bstart = fbstart(file) = dbstart(dentry); bend = fbend(file) = dbend(dentry); size = sizeof(struct file *) * sbmax(sb); UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->lower_files)) { err = -ENOMEM; goto out; } size = sizeof(int) * sbmax(sb); UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { err = -ENOMEM; goto out; } if (S_ISDIR(dentry->d_inode->i_mode)) { /* We need to open all the files. */ err = open_all_files(file); if (err) goto out; } else { int new_brid; /* We only open the highest priority branch. */ err = open_highest_file(file, willwrite); if (err) goto out; new_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; if (unlikely(new_brid != orig_brid && sbgen > fgen)) { /* * If we re-opened the file on a different branch * than the original one, and this was due to a new * branch inserted, then update the mnt counts of * the old and new branches accordingly. */ unionfs_mntget(dentry, bstart); unionfs_mntput(sb->s_root, branch_id_to_idx(sb, orig_brid)); } /* regular files have only one open lower file */ fbend(file) = fbstart(file); } atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(dentry->d_inode)->generation)); out_may_copyup: /* Copyup on the first write to a file on a readonly branch. */ if (willwrite && IS_WRITE_FLAG(file->f_flags) && !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && is_robranch(dentry)) { pr_debug("unionfs: do delay copyup of \"%s\"\n", dentry->d_name.name); err = do_delayed_copyup(file, parent); /* regular files have only one open lower file */ if (!err && !S_ISDIR(dentry->d_inode->i_mode)) fbend(file) = fbstart(file); } out: if (err) { kfree(UNIONFS_F(file)->lower_files); kfree(UNIONFS_F(file)->saved_branch_ids); } return err; }