[C] copia ricorsiva albero direttori

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Zanarkand
Prode Principiante
Messaggi: 26
Iscrizione: domenica 18 dicembre 2016, 20:11
Sesso: Maschile

[C] copia ricorsiva albero direttori

Messaggio da Zanarkand »

Ecco cosa ho tirato fuori. Premetto che sono alle prime armi e avrei bisogno di fare più domande per chiarire i miei dubbi.
Intanto ecco il codice:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define N 100
#define BUFFSIZE 4096

void recursiveCopyDirectoryTree(char* source, char* dest);

int main(int argc, char** argv)
{
  DIR* destDP;

  if(mkdir(argv[2], S_IRUSR | S_IWUSR | S_IXUSR)==-1)
    {
      printf("\nmkdir error!");
      exit(1);
    }

  if((destDP=opendir(argv[2]))==NULL)
    {
      printf("\nopendir error!");
      exit(1);
    }

  recursiveCopyDirectoryTree(argv[1], argv[2]);

  if(closedir(destDP)==-1)
    {
      printf("\nclosedir error!");
      exit(1);
    }

  return 0;
}

void recursiveCopyDirectoryTree(char* source, char* dest)
{
  int nR, nW, sourceFD, destFD;
  struct stat statBuf;
  struct dirent* entry;
  char sourceFullName[N], destFullName[N], buf[BUFFSIZE];
  DIR* sourceDP;
  DIR* destDP;

  if(lstat(source, &statBuf)==-1)
    {
      printf("\nlstat error!");
      exit(1);
    }

  if(S_ISDIR(statBuf.st_mode)==0)
    {
      printf("\nsource is not a directory!");
      exit(1);
    }

  if((sourceDP=opendir(source))==NULL)
    {
      printf("\nopendir error!");
      exit(1);
    }

  while((entry=readdir(sourceDP))!=NULL)
    {
      if(strcmp(entry->d_name, ".")!=0 && strcmp(entry->d_name, "..")!=0)
	{
	  sprintf(sourceFullName, "%s/%s", source, entry->d_name);
	  sprintf(destFullName, "%s/%s", dest, entry->d_name);

	  if(lstat(sourceFullName, &statBuf)==-1)
	    {
	      printf("\nlstat error!");
	      exit(1);
	    }

	  if(S_ISREG(statBuf.st_mode))
	    {
	      sourceFD=open(sourceFullName, O_RDONLY);
	      destFD=open(destFullName, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,                  S_IWUSR | S_IRUSR | S_IXUSR);

	      if(sourceFD==-1 || destFD==-1)
		{
		  printf("\nerror opening a file!");
		  exit(1);
		}

	      while((nR=read(sourceFD, buf, sizeof(buf)))>0)
		{
		  nW=write(destFD, buf, nR);
		  if(nW!=nR)
		    {
		      printf("\nerror: read %d, wrote %d", nR, nW);
		      exit(1);
		    }

		  if(nR<0)
		    {
		      printf("\nwrite error!");
		      exit(1);
		    }

		  close(sourceFD);
		  close(destFD);
		}
	      }
	      else
		{
		  if(mkdir(destFullName, S_IRUSR | S_IWUSR | S_IXUSR)==-1)
		    {
		      printf("\nmkdir error!");
		      exit(1);
		    }

		  if((destDP=opendir(destFullName))==NULL)
		    {
		      printf("\nopendir error!");
		      exit(1);
		    }

		  recursiveCopyDirectoryTree(sourceFullName, destFullName);

		  closedir(sourceDP);
		  closedir(destDP);	    
		}
	    }
	}
    }
Non funziona al 100% poiché provandolo riesce a copiare soltanto 1 sottodirettorio su 3 presenti e alcuni file (copiandoli bene). Inoltre non termina il programma ma sembra che ricorra all'infinito anche se il risultato è sempre quello. Sono quindi costretto a chiudere il terminale per riavere il controllo di esso.
Cosa ho sbagliato?

Inoltre avrei un po' di domande su alcuni particolari che mi potrebbero aiutare in futuro:
1) la mkdir iniziale nel main che crea la cartella di destinazione provoca l'uscita dal programma se la cartella è già presente.. come evito ciò?
2) il pezzo di codice che legge dai file "source" e scrive nei file "dest" l'ho preso da alcune slide ma non capisco il controllo che viene subito dopo il while (che nella slide viene spiegato con "controllo sull'ultima lettura").
3) nel caso volessi copiare dei file binari il buf di che tipo sarà? in questo caso sono dei file di testo quindi mi sembra scontato che bisogna allocare dei buf di tipo char.
4) ieri, non so per quale motivo, mi dava problemi la opendir fatta nel main e provando ad eliminarla il programma funzionava comunque alla stessa maniera. Quindi l'opendir ha senso dopo aver creato una nuova cartella o no?
5) per il terzo argomento della open si può scrivere (nel caso in cui volessi dare tutti i permessi allo user) soltanto S_IRWXUSR al posto di separarli con l'or bit a bit?
Grazie per l'aiuto!!
ixamit
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 499
Iscrizione: giovedì 14 novembre 2013, 10:16

Re: [C] copia ricorsiva albero direttori

Messaggio da ixamit »

1) la mkdir iniziale nel main che crea la cartella di destinazione provoca l'uscita dal programma se la cartella è già presente.. come evito ciò?
si puo' verificare che errno sia diverso da EEXIT, ma questo e' valido per tutti i file non solo le directory
2) il pezzo di codice che legge dai file "source" e scrive nei file "dest" l'ho preso da alcune slide ma non capisco il controllo che viene subito dopo il while (che nella slide viene spiegato con "controllo sull'ultima lettura").
l'indentazione e' poco chiara... comunque la read restituisce il numero di bytes letti o zero per fine file.
-1 indica un errore
3) nel caso volessi copiare dei file binari il buf di che tipo sarà? in questo caso sono dei file di testo quindi mi sembra scontato che bisogna allocare dei buf di tipo char.
solitamente lo stesso char
4) ieri, non so per quale motivo, mi dava problemi la opendir fatta nel main e provando ad eliminarla il programma funzionava comunque alla stessa maniera. Quindi l'opendir ha senso dopo aver creato una nuova cartella o no?
la opendir restituisce un puntatore alla directory passata, la open un nuovo fd
5) per il terzo argomento della open si può scrivere (nel caso in cui volessi dare tutti i permessi allo user) soltanto S_IRWXUSR al posto di separarli con l'or bit a bit?
si, ma considera che sarebbe meglio copiarli dal file sorgente:

Codice: Seleziona tutto

open(destFullName, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,  
statBuf.st_mode); <<----- dallo stat del sorgente
Zanarkand
Prode Principiante
Messaggi: 26
Iscrizione: domenica 18 dicembre 2016, 20:11
Sesso: Maschile

Re: [C] copia ricorsiva albero direttori

Messaggio da Zanarkand »

ho solo intravisto l'uso di errno all'interno di alcuni codici sul web..puoi spiegarmi meglio come usarlo insieme ad EEXIT?
ma il campo st_mode della struct stat non identifica il tipo di dato? file, directory, link o socket? o ci sono anche i permessi all'interno?
ixamit
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 499
Iscrizione: giovedì 14 novembre 2013, 10:16

Re: [C] copia ricorsiva albero direttori

Messaggio da ixamit »

ho solo intravisto l'uso di errno all'interno di alcuni codici sul web..puoi spiegarmi meglio come usarlo insieme ad EEXIT?
devi includere errno.h e verificare l'errore con qualcosa di simile:

Codice: Seleziona tutto

    ...
    value = mkdir(dir,0755);
    if (value == -1) 
    {   
        if (errno == EEXIST)
            LOG("%s exist",dir);
        else
            perror("mkdir");
    }
    ...   
oppure meglio fare una lstat e in caso di errore ENOENT (No such file or directory) eseguire la mkdir
ma il campo st_mode della struct stat non identifica il tipo di dato? file, directory, link o socket? o ci sono anche i permessi all'interno?
Si certamente, di seguito le maschere:

Codice: Seleziona tutto

           S_IRWXU     00700   mask for file owner permissions
           S_IRUSR     00400   owner has read permission
           S_IWUSR     00200   owner has write permission
           S_IXUSR     00100   owner has execute permission

           S_IRWXG     00070   mask for group permissions
           S_IRGRP     00040   group has read permission

           S_IWGRP     00020   group has write permission
           S_IXGRP     00010   group has execute permission

           S_IRWXO     00007   mask for permissions for others
                               (not in group)
           S_IROTH     00004   others have read permission
           S_IWOTH     00002   others have write permission
           S_IXOTH     00001   others have execute permission

volendo essere precisi sono i primi 9 bit del campo st_mode
Zanarkand
Prode Principiante
Messaggi: 26
Iscrizione: domenica 18 dicembre 2016, 20:11
Sesso: Maschile

Re: [C] copia ricorsiva albero direttori

Messaggio da Zanarkand »

grazie in questi giorni approfondirò questi aspetti.
Comunque ho risolto il problema principale del codice. In pratica non finiva di copiare tutti i file poiché chiudevo le cartelle appena ritornava dalla ricorsione invece di chiuderle dopo il while (in pratica alla fine dell'istanza). Quindi in pratica il programma ritornando dalla prima ricorsione chiudeva le cartelle source e dest di fatto bloccando tutto.
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 13 ospiti