File: faked.cc

package info (click to toggle)
fakeroot 0.4.4-9.2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 696 kB
  • ctags: 173
  • sloc: sh: 7,643; ansic: 711; cpp: 312; makefile: 120
file content (522 lines) | stat: -rw-r--r-- 13,543 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
/*
  copyright   : GPL
  title       : fakeroot
  description : create a "fake" root shell, by wrapping 
                functions like chown, stat, etc. Useful for debian
                packaging mechanism
  author      : joost witteveen, joostje@debian.org
*/

/*
  upon startup, the fakeroot script (/usr/bin/fakeroot) 
  forks faked (this program), and the shell or user program that
  will run with the libtricks.so.0.0 wrapper.
  
  These tree running programs have the following tasks:
    
    fakeroot scripot
       starts the other two processes, waits for the user process to
       die, and then send a SIGTERM signal to faked, causing
       Faked to clear the ipc message queues.

    faked
       the ``main'' daemon, creates ipc message queues, and later
       receives ipc messages from the user program, maintains
       fake inode<->ownership database (actually just a 
       lot of struct stat entries). Will clear ipc message ques
       upon receipt of a SIGTERM. Will show debug output upon
       receipt of a SIGUSR1 (if started with -d debug option)

    user program
       Any shell or other programme, run with 
       LD_PRELOAD=libtricks.so.0.0, and FAKEROOTKEY=ipc-key,
       thus the executed commands will communicate with
       faked. libtricks will wrap all file ownership etc modification
       calls, and send the info to faked. Also the stat() function
       is wrapped, it will first ask the database kept by faked
       and report the `fake' data if available.

  The following functions are currently wrapped:
     getuid(), geteuid(), getgid(), getegid(),
     mknod()
     chown(), fchown() lchown()
     chmod(), fchmod() 
     mkdir(),
     lstat(), fstat(), stat() (actually, __xlstat, ...)
     unlink(), remove(), rmdir(), rename()
    
  comments:
    I need to wrap unlink because of the following:
        install -o admin foo bar
	rm bar
        touch bar         //bar now may have the same inode:dev as old bar,
	                  //but unless the rm was caught,
			  //fakeroot still has the old entry.
        ls -al bar
    Same goes for all other ways to remove inodes form the filesystem,
    like rename(existing_file, any_file).

    The communication between client (user progamme) and faked happens 
    with inode/dev information, not filenames. This is 
    needed, as the client is the only one who knows what cwd is,
    so it's much easier to stat in the client. Otherwise, the daemon
    needs to keep a list of client pids vs cwd, and I'd have to wrap
    fork e.d., as they inherit their parent's cwd. Very compilcated.
    
    */			       
/* ipc documentation bugs: msgsnd(2): MSGMAX=4056, not 4080 
   (def in ./linux/msg.h, couldn't find other def in /usr/include/ 
   */


#include "communicate.h"
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <string.h>
#include <iostream.h>
#include <signal.h>


#if HAVE_SEMUN_DEF == 0
  union semun {
    int val;
    struct semid_ds *buf;
    u_short *array;
  };
#endif

void process_chown(struct fake_msg *buf);
void process_chmod(struct fake_msg *buf);
void process_mknod(struct fake_msg *buf);
void process_stat(struct fake_msg *buf);
void process_unlink(struct fake_msg *buf);

bool operator <(const struct fakestat &a, const struct fakestat &b);

typedef void (*process_func)(struct fake_msg *);

process_func func_arr[]={process_chown,
			 process_chmod,
			 process_mknod,
			 process_stat,
			 process_unlink,
			 NULL};


key_t msg_key=0;
//static int msg_get,msg_send, sem_id;
bool debug=false;


// our main (and only) data base, contains all inodes and other info:
bool operator <(const struct fakestat &a, const struct fakestat &b){
  
  if(a.ino<b.ino)
    return true;
  else if(a.ino> b.ino)
    return false;
  else 
    return a.dev<b.dev;
};

set <struct fakestat, less<struct fakestat> > data;




/*********************************/
/*                               */
/* data base maintainance        */
/*                               */
/*********************************/
void debug_stat(const struct fakestat *st){
  fprintf(stderr,"dev:ino=(%lx:%li), mode=0%lo, own=(%li,%li), nlink=%li, rdev=%li\n",
	  st->dev,
	  st->ino,
	  st->mode,
	  st->uid,
	  st->gid,
	  st->nlink,
	  st->rdev);
}


/*int init_get_msg(){
  return 1;
}
key_t get_ipc_key(){
  return msg_key;
}
*/
void insert_or_overwrite(set <struct fakestat, less<struct fakestat> > &data,
			 struct fakestat &st){
  // just doing a data.insert(stat) would not update data
  // of inodes that are already present (if operator compares "equal",
  // stat isn't writen).

  set <struct fakestat, less<struct fakestat> >::iterator i;

  
  i=data.find(st);
  if(i==data.end()){
    if(debug){
      fprintf(stderr,"FAKEROOT: insert_or_overwrite unknown stat:\n");
      debug_stat(&st);
    }
    data.insert(st);
  }
  else
    memcpy(((struct fakestat *)&(*i)),&st,sizeof(st));
}


/*void copy_stat(struct fakestat *dest, const struct stat *source){
  //copy only the things we fake (i.e. "know better"), and leave
  //the other info (like file size etc).
    dest->st_dev =source->st_dev;
    dest->st_rdev=source->st_rdev;
    dest->st_ino =source->st_ino;
    dest->st_uid =source->st_uid;
    dest->st_gid =source->st_gid;
    dest->st_mode=source->st_mode;
}
*/

/*******************************************/
/*                                         */
/* process requests from wrapper functions */
/*                                         */
/*******************************************/


void process_chown(struct fake_msg *buf){
  struct fakestat *stptr;
  struct fakestat st;
  set <struct fakestat, less<struct fakestat> >::iterator i;
  
  if(debug){
    fprintf(stderr,"FAKEROOT: chown ");
    debug_stat(&buf->st);
  }
  i=data.find(buf->st);
  if(i!=data.end()){
    stptr=(struct fakestat *)&(*i); //to remove the const type.
    /* From chown(2): If  the owner or group is specified as -1, 
       then that ID is not changed. 
       Cannot put that test in libtricks, as at that point it isn't
       known what the fake user/group is (so cannot specify `unchanged')
       
       I typecast to (u_int32_t), as st.uid may be bigger than uid_t.
       In that case, the msb in st.uid should be discarded.
       I don't typecaset to (uid_t), as the size of uid_t may vary
       depending on what libc (headers) were used to compile. So,
       different clients might actually use different uid_t's
       concurrently. Yes, this does seem farfeched, but was
       actually the case with the libc5/6 transition.
    */
    if ((u_int32_t)buf->st.uid != (u_int32_t)-1) 
      stptr->uid=buf->st.uid;
    if ((u_int32_t)buf->st.gid != (u_int32_t)-1)
      stptr->gid=buf->st.gid;
  }
  else{
    st=buf->st;
    /* See comment above.  We pretend that unknown files are owned
       by root.root, so we have to maintain that pretense when the
       caller asks to leave an id unchanged. */
    if ((u_int32_t)st.uid == (u_int32_t)-1)
       st.uid = 0;
    if ((u_int32_t)st.gid == (u_int32_t)-1)
       st.gid = 0;
    insert_or_overwrite(data,st);
  }
}

void process_chmod(struct fake_msg *buf){
  struct fakestat *st;
  set <struct fakestat, less<struct fakestat> >::iterator i;
  
  if(debug)
    fprintf(stderr,"FAKEROOT: chmod, mode=%lo\n",
	    buf->st.mode);
  
  i=data.find(buf->st);
  if(i!=data.end()){
    st=(struct fakestat *)&(*i);
    st->mode = (buf->st.mode&~S_IFMT) | (st->mode&S_IFMT);
  }
  else{
    st=&buf->st;
    st->uid=0;
    st->gid=0;
  }
  insert_or_overwrite(data,*st);
}
void process_mknod(struct fake_msg *buf){
  struct fakestat *st;
  set <struct fakestat, less<struct fakestat> >::iterator i;
  
  if(debug)
    fprintf(stderr,"FAKEROOT: chmod, mode=%lo\n",
	    buf->st.mode);
  
  i=data.find(buf->st);
  if(i!=data.end()){
    st=(struct fakestat *)&(*i);
    st->mode = buf->st.mode;
    st->rdev = buf->st.rdev;
  }
  else{
    st=&buf->st;
    st->uid=0;
    st->gid=0;
  }
  insert_or_overwrite(data,*st);
}

void process_stat(struct fake_msg *buf){
  set <struct fakestat, less<struct fakestat> >::iterator i;

  i=data.find(buf->st);
  if(debug){
    fprintf(stderr,"FAKEROOT: process stat oldstate=");
    debug_stat(&buf->st);
  }
  if(i==data.end()){
    if (debug)
      fprintf(stderr,"FAKEROOT:    (previously unknown)\n");
    buf->st.uid=0;
    buf->st.gid=0;
  }
  else{
    cpyfakefake(&buf->st,&(*i));
    if(debug){
      fprintf(stderr,"FAKEROOT: (previously known): fake=");
      debug_stat(&buf->st);      
    }

  }
  send_fakem(buf);
}
//void process_fstat(struct fake_msg *buf){
//  process_stat(buf);
//}

void process_unlink(struct fake_msg *buf){

  if((buf->st.nlink==1)||
     (S_ISDIR(buf->st.mode)&&(buf->st.nlink==2))){
    set <struct fakestat, less<struct fakestat> >::iterator i;
    i=data.find(buf->st);
    if(i!=data.end()){
      if(debug){
	fprintf(stderr,"FAKEROOT: unlink known file, old stat=");
	debug_stat(&(*i));
      }
      data.erase(i);
    }
    if(data.find(buf->st)!=data.end()){
      fprintf(stderr,"FAKEROOT************************************************* cannot remove stat (a \"cannot happen\")\n");
    }
  }
}

void debugdata(int){
  set <struct fakestat, less<struct fakestat> >::iterator i;

  fprintf(stderr," FAKED keeps data of %i inodes:\n",data.size());
  for(i=data.begin(); i!=data.end(); i++)
    debug_stat(&(*i));
}


void process_msg(struct fake_msg *buf){

  func_id f;
  f= buf->id;
  func_arr[f]((struct fake_msg*)buf);
  
}

void get_msg(){

  struct fake_msg buf;
  int r=0;

  if(debug)
    fprintf(stderr,"FAKEROOT: msg=%i, key=%i\n",msg_get,msg_key);
  do{
    r=msgrcv(msg_get,&buf,sizeof(fake_msg),0,0);
    if(debug)
      fprintf(stderr,"FAKEROOT: r=%i, received message type=%li, message=%i\n",r,buf.mtype,buf.id);
    if(r!=-1)
      process_msg(&buf);
  }while ((r!=-1)||(errno==EINTR));
  if(debug){
    perror("FAKEROOT, get_msg");
    fprintf(stderr,"r=%i, EINTR=%i\n",errno,EINTR);
  }
}

/***********/
/*         */
/* misc    */
/*         */
/***********/



void cleanup(int g){
  union semun sem_union;
  if(debug)
    fprintf(stderr, "fakeroot: clearing up message queues and semaphores,"
	    " signal=%i\n",  g);
  msgctl (msg_get, IPC_RMID,NULL);
  msgctl (msg_snd, IPC_RMID,NULL);
  semctl (sem_id,0,IPC_RMID,sem_union);
  if(g!=-1)
    exit(0);
}

/*************/
/*           */
/*   main    */
/*           */
/*************/


static long int read_intarg(char **argv){
  if(!*argv){
    fprintf(stderr,"%s needs numeric argument\n",*(argv-1));
    exit(1);
  } else{
    return atoi(*argv);
  }
}

int main(int /*argc*/, char **argv){
  union semun sem_union;
  struct sigaction sa,sa_debug;
  int i;
  bool justcleanup=false;
  bool foreground=false;
  int pid;

  if(getenv(FAKEROOTKEY_ENV)){
    //(I'm not sure -- maybe this can work?)
    fprintf(stderr,"Please, don't run fakeroot from within fakeroot!\n");
    exit(1);
  }
  while(*(++argv)){
    if(!strcmp(*argv,"--key"))
      msg_key=read_intarg(++argv);
    else if(!strcmp(*argv,"--cleanup")){
      msg_key=read_intarg(++argv);
      justcleanup=true;
    }
    else if(!strcmp(*argv,"--foreground"))
      foreground=true;
    else if(!strcmp(*argv,"--debug"))
      debug=true;
    else {
      fprintf(stderr,"faked, daemon for fake root enfironment\n");
      fprintf(stderr,"Best used from the shell script `fakeroot'\n");
      fprintf(stderr,"options for fakeroot: --key, --cleanup, --foreground, --debug\n");
      exit(1);
    }
  }
  if(!msg_key){
    srandom(time(NULL)+getpid()*33151);
    while(!msg_key && (msg_key!=-1))  /* values 0 and -1 are treated
					 specially by libfake */
      msg_key=random();
    
  }
  if(debug)
    fprintf(stderr,"using %i as msg key\n",msg_key);
  
  msg_get=msgget(msg_key,IPC_CREAT|0600);
  msg_snd=msgget(msg_key+1,IPC_CREAT|0600);
  sem_id=semget(msg_key+2,1,IPC_CREAT|0600);
  sem_union.val=1;
  semctl (sem_id,0,SETVAL,sem_union);

  if((msg_get==-1)||(msg_snd==-1)||(sem_id==-1)){
    perror("fakeroot, while creating message channels");
    cleanup(-1);
    exit(1);
  }
  if(debug)
    fprintf(stderr,"msg_key=%i\n",msg_key);

  if(justcleanup)
    cleanup(0);

  sa.sa_handler=cleanup;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags=0;
  //  sa.sa_restorer=0;


  sa_debug.sa_handler=debugdata;
  sigemptyset(&sa_debug.sa_mask);
  sa_debug.sa_flags=0;
  //  sa_debug.sa_restorer=0;

  for(i=1; i< NSIG; i++){
    switch (i){
    case SIGKILL:
    case SIGTSTP:
    case SIGCONT:
      break;
    case SIGUSR1:
    case SIGUSR2:
      sigaction(i,&sa_debug,NULL);
      break;
    default:
      sigaction(i,&sa,NULL);
      break;
    }
  }
  


  if(!foreground){
    /* literally copied from the linux klogd code, go to background */
    if ((pid=fork()) == 0){
      int fl;
      int num_fds = getdtablesize();
      
      fflush(stdout);

      /* This is the child closing its file descriptors. */
      for (fl= 0; fl <= num_fds; ++fl)
	close(fl);
      setsid();
    } else{
      printf("%i:%i\n",msg_key,pid);

      exit(0);
    }
  } else{
    printf("%i:%i\n",msg_key,getpid());
    fflush(stdout);
  }
  get_msg();    /* we shouldn't return from this function */

  cleanup(-1);  /* if we do return, try to clean up and exit with a nonzero
		   return status */
  return 1;
}